Split the adventures router into a package
`backend/app/routers/adventures.py` held 2353 lines and 35 endpoints. It is now a package of 14 modules, the largest 443 lines. The split moves text rather than rewriting it. An AST comparison against the old file confirms all 86 definitions are identical, and the OpenAPI schema still lists the same 35 operations. Names a test replaces now live in `turns.py` only, and other modules reach them as `turns.<name>`. Rebinding a re-exported alias changes the alias and leaves every caller reading the original, so the package root does not re-export them. A patch aimed at the old target raises `AttributeError` instead of passing while doing nothing. Tests and the fixtures in `backend/tools/` say `adventures.turns.<name>`. The same rule keeps the turn lock working. One module owns `_active_turns`, so one lock guards one set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Dix4oGV3njgWRdu7P9t6r
This commit is contained in:
co-authored by
Claude Opus 5
parent
32cd7c1077
commit
2fa812c056
@@ -60,7 +60,7 @@ def client(monkeypatch):
|
||||
ScriptedProvider.replies = ["Attempt one."]
|
||||
ScriptedProvider.calls = 0
|
||||
ScriptedProvider.prompts = []
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -76,7 +76,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ def client(monkeypatch):
|
||||
ScriptedProvider.replies = ["A reply."]
|
||||
ScriptedProvider.calls = 0
|
||||
ScriptedProvider.prompts = []
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -86,7 +86,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def client(monkeypatch):
|
||||
|
||||
ScriptedProvider.replies = ["Attempt one.", "Attempt two.", "Next turn."]
|
||||
ScriptedProvider.calls = 0
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -70,7 +70,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ def client(monkeypatch):
|
||||
|
||||
ScriptedProvider.replies = ["A reply."]
|
||||
ScriptedProvider.calls = 0
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -93,7 +93,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ def client(monkeypatch):
|
||||
ScriptedProvider.replies = ["Attempt one."]
|
||||
ScriptedProvider.calls = 0
|
||||
ScriptedProvider.prompts = []
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -72,7 +72,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -366,9 +366,9 @@ def test_refresh_is_rejected_while_a_turn_is_generating(client):
|
||||
|
||||
sid = make_scenario(client, title="Keep", memory="Old")
|
||||
adv_id = start(client, sid)
|
||||
adventures._active_turns.add(adv_id)
|
||||
adventures.turns._active_turns.add(adv_id)
|
||||
try:
|
||||
r = client.post(f"/api/adventures/{adv_id}/refresh", json={})
|
||||
assert r.status_code == 409
|
||||
finally:
|
||||
adventures._active_turns.discard(adv_id)
|
||||
adventures.turns._active_turns.discard(adv_id)
|
||||
|
||||
@@ -33,7 +33,7 @@ def db():
|
||||
finally:
|
||||
session.close()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
|
||||
|
||||
def _make_adventure(db, script_state):
|
||||
@@ -130,15 +130,15 @@ def test_undo_blocked_by_active_turn_lock(db):
|
||||
_add(db, adv, 1, "ai", state_after={})
|
||||
db.commit()
|
||||
|
||||
adventures.acquire_turn_lock(adv.id) # a turn is "generating"
|
||||
adventures.turns.acquire_turn_lock(adv.id) # a turn is "generating"
|
||||
try:
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
adventures.undo_turn(adv.id, db=db, user=user)
|
||||
assert exc.value.status_code == 409
|
||||
# The failed undo must not have released someone else's lock.
|
||||
assert adv.id in adventures._active_turns
|
||||
assert adv.id in adventures.turns._active_turns
|
||||
finally:
|
||||
adventures._active_turns.discard(adv.id)
|
||||
adventures.turns._active_turns.discard(adv.id)
|
||||
|
||||
|
||||
def test_undo_prunes_memory_covering_removed_actions(db):
|
||||
@@ -221,12 +221,12 @@ def test_retry_restores_the_state_the_turn_started_from(db, monkeypatch):
|
||||
db.commit()
|
||||
|
||||
monkeypatch.setattr(adventures.limits, "rate_limit", lambda *a, **k: None)
|
||||
monkeypatch.setattr(adventures, "check_demo_cap", lambda *a, **k: None)
|
||||
monkeypatch.setattr(adventures.turns, "check_demo_cap", lambda *a, **k: None)
|
||||
|
||||
async def _noop(*a, **k):
|
||||
if False:
|
||||
yield # make it an async generator
|
||||
monkeypatch.setattr(adventures, "generate_turn", _noop)
|
||||
monkeypatch.setattr(adventures.turns, "generate_turn", _noop)
|
||||
|
||||
adventures.retry_action(adv.id, request=None, db=db, user=user)
|
||||
|
||||
@@ -238,4 +238,4 @@ def test_retry_restores_the_state_the_turn_started_from(db, monkeypatch):
|
||||
assert last.live is True
|
||||
assert last.variant_count == 0
|
||||
assert last.state_after == {"gold": 20} # its own outcome, untouched
|
||||
adventures._active_turns.discard(adv.id)
|
||||
adventures.turns._active_turns.discard(adv.id)
|
||||
|
||||
@@ -80,7 +80,7 @@ def _make_world(monkeypatch, *, seeded_actions: int = 0):
|
||||
ScriptedProvider.replies = ["A reply."]
|
||||
ScriptedProvider.calls = 0
|
||||
ScriptedProvider.prompts = []
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -102,7 +102,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ def long_client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ def client(monkeypatch):
|
||||
|
||||
ScriptedProvider.replies = [f"Take {n}." for n in range(1, 40)]
|
||||
ScriptedProvider.calls = 0
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -72,7 +72,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ def client(monkeypatch):
|
||||
# Distinct replies so a take can be told apart from its siblings by text.
|
||||
ScriptedProvider.replies = [f"Take {n}." for n in range(1, 40)]
|
||||
ScriptedProvider.calls = 0
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(
|
||||
auth,
|
||||
"resolve_provider_config",
|
||||
@@ -76,7 +76,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ def client(monkeypatch):
|
||||
|
||||
ScriptedProvider.replies = [f"Take {n}." for n in range(1, 40)]
|
||||
ScriptedProvider.calls = 0
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -83,7 +83,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ def client(monkeypatch):
|
||||
|
||||
# Force a real, non-demo turn that uses the fake provider.
|
||||
ScriptedProvider.replies = [AI_REPLY]
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -69,7 +69,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import pytest
|
||||
from fastapi import Depends
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app import auth, limits, models
|
||||
from app import auth, limits, models, worldstate
|
||||
from app.database import Base, SessionLocal, engine, get_db
|
||||
from app.main import app
|
||||
from app.routers import adventures
|
||||
@@ -50,7 +50,7 @@ def client(monkeypatch):
|
||||
setup.flush()
|
||||
adv = models.Adventure(
|
||||
user_id=user.id, scenario_id=scenario.id, title="Run",
|
||||
world_state=adventures.worldstate.instantiate(SCHEMA),
|
||||
world_state=worldstate.instantiate(SCHEMA),
|
||||
)
|
||||
setup.add(adv)
|
||||
setup.flush()
|
||||
@@ -62,7 +62,7 @@ def client(monkeypatch):
|
||||
setup.close()
|
||||
|
||||
ScriptedProvider.replies = [AI_REPLY]
|
||||
monkeypatch.setattr(adventures, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(adventures.turns, "OpenAICompatibleProvider", ScriptedProvider)
|
||||
monkeypatch.setattr(auth, "resolve_provider_config", lambda s: auth.ProviderConfig(
|
||||
"http://fake", "k", "test-model", False))
|
||||
monkeypatch.setattr(limits, "rate_limit", lambda *a, **k: None)
|
||||
@@ -79,7 +79,7 @@ def client(monkeypatch):
|
||||
yield c
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
adventures._active_turns.clear()
|
||||
adventures.turns._active_turns.clear()
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user