fix(core): a turn cut by shutdown keeps running_turn for recover
This commit is contained in:
@@ -825,6 +825,7 @@ class Conversations:
|
|||||||
item_origin=item_origin,
|
item_origin=item_origin,
|
||||||
)
|
)
|
||||||
stop = "error"
|
stop = "error"
|
||||||
|
cut = False
|
||||||
try:
|
try:
|
||||||
events = backend.complete(
|
events = backend.complete(
|
||||||
agent=self._claude_agent(conv.agent_name),
|
agent=self._claude_agent(conv.agent_name),
|
||||||
@@ -841,9 +842,12 @@ class Conversations:
|
|||||||
async for event in events:
|
async for event in events:
|
||||||
yield event
|
yield event
|
||||||
stop = "interrupted" if capture.interrupted else "end_turn"
|
stop = "interrupted" if capture.interrupted else "end_turn"
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
cut = True
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
runner.turn_id = None
|
runner.turn_id = None
|
||||||
await self._mark_done(conv, capture)
|
await self._mark_done(conv, capture, cut=cut)
|
||||||
self._bus.publish(
|
self._bus.publish(
|
||||||
"turn.end",
|
"turn.end",
|
||||||
conversation_id=conv.external_id,
|
conversation_id=conv.external_id,
|
||||||
@@ -1173,8 +1177,13 @@ class Conversations:
|
|||||||
|
|
||||||
await self._update(conv, apply)
|
await self._update(conv, apply)
|
||||||
|
|
||||||
async def _mark_done(self, conv: Conversation, capture: TurnCapture) -> None:
|
async def _mark_done(
|
||||||
|
self, conv: Conversation, capture: TurnCapture, *, cut: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""Close the turn; a cancelled one keeps ``running_turn`` for ``recover``."""
|
||||||
|
|
||||||
async def apply(row: Conversation) -> None:
|
async def apply(row: Conversation) -> None:
|
||||||
|
if not cut:
|
||||||
row.running_turn = None
|
row.running_turn = None
|
||||||
row.last_activity_at = datetime.now(UTC)
|
row.last_activity_at = datetime.now(UTC)
|
||||||
if capture.session_id is not None:
|
if capture.session_id is not None:
|
||||||
|
|||||||
@@ -496,6 +496,22 @@ async def test_recover_closes_open_tool_use_and_injects_interrupted(
|
|||||||
assert ScriptedClient.instances == []
|
assert ScriptedClient.instances == []
|
||||||
|
|
||||||
|
|
||||||
|
async def test_graceful_stop_keeps_running_turn_for_recover(world: World) -> None:
|
||||||
|
conv = await world.conversations.create(kind="master", agent="a", origin="test")
|
||||||
|
ScriptedClient.hold = asyncio.Event()
|
||||||
|
await world.conversations.post(conv, "long")
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
assert (await world.conversations.get(conv.external_id)).running_turn is not None
|
||||||
|
await world.conversations.stop()
|
||||||
|
row = await world.conversations.get(conv.external_id)
|
||||||
|
assert row.running_turn is not None
|
||||||
|
cut = await world.conversations.recover()
|
||||||
|
assert [c.external_id for c in cut] == [conv.external_id]
|
||||||
|
assert (await world.conversations.get(conv.external_id)).running_turn is None
|
||||||
|
assert await world.statuses(conv) == [("user", "interrupted"), ("normal", "queued")]
|
||||||
|
ScriptedClient.hold = None
|
||||||
|
|
||||||
|
|
||||||
async def test_read_and_bindings(world: World) -> None:
|
async def test_read_and_bindings(world: World) -> None:
|
||||||
sid = str(uuid.uuid4())
|
sid = str(uuid.uuid4())
|
||||||
history = [
|
history = [
|
||||||
|
|||||||
Reference in New Issue
Block a user