mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 15:37:55 +08:00
cli/state: cancel a compact's summary instead of hanging on it
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING, Literal, overload
|
||||
|
||||
import pytest
|
||||
@@ -57,6 +58,31 @@ class SummaryClient:
|
||||
)
|
||||
|
||||
|
||||
class HangingClient:
|
||||
"""A summarizer that never answers until the request is cancelled."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.entered = asyncio.Event()
|
||||
|
||||
@overload
|
||||
async def chat_completions(
|
||||
self, param: ChatCompletionsParam, *, stream: Literal[False] = False
|
||||
) -> ChatCompletionResponse: ...
|
||||
|
||||
@overload
|
||||
async def chat_completions(
|
||||
self, param: ChatCompletionsParam, *, stream: Literal[True]
|
||||
) -> AsyncIterator[ChatCompletionChunk]: ...
|
||||
|
||||
async def chat_completions(
|
||||
self, param: ChatCompletionsParam, *, stream: bool = False
|
||||
) -> ChatCompletionResponse | AsyncIterator[ChatCompletionChunk]:
|
||||
del param, stream
|
||||
self.entered.set()
|
||||
await asyncio.Event().wait() # until the caller cancels
|
||||
raise AssertionError("the summary request should have been cancelled")
|
||||
|
||||
|
||||
async def test_compact_to_new_session(tmp_path: Path) -> None:
|
||||
memory = await MemoryStore.open(DatabaseConfig())
|
||||
try:
|
||||
@@ -128,6 +154,50 @@ async def test_compact_to_new_session(tmp_path: Path) -> None:
|
||||
await memory.close()
|
||||
|
||||
|
||||
async def test_compact_cancel_leaves_the_session_untouched(tmp_path: Path) -> None:
|
||||
"""Ctrl+C during the summary drops the request before anything is created."""
|
||||
memory = await MemoryStore.open(DatabaseConfig())
|
||||
try:
|
||||
provider = OpenAIProvider(access_key_or_token="sk-test")
|
||||
config = ConfigStore(path=tmp_path / "plyngent.toml", document=tomlkit.document())
|
||||
config.providers = {"local": provider}
|
||||
state = ReplState(
|
||||
config=config,
|
||||
memory=memory,
|
||||
workspace=tmp_path,
|
||||
provider_name="local",
|
||||
provider=provider,
|
||||
model="gpt-test",
|
||||
tools_enabled=False,
|
||||
)
|
||||
await state.new_session("orig")
|
||||
old_id = state.session_id
|
||||
assert old_id is not None
|
||||
client = HangingClient()
|
||||
state.client = client
|
||||
state.agent = ChatAgent(
|
||||
state.client,
|
||||
model=state.model,
|
||||
memory=state.memory,
|
||||
session_id=old_id,
|
||||
system_prompt="Be brief.",
|
||||
)
|
||||
state.agent.messages = [UserChatMessage(content="do work")]
|
||||
before = {row.sid for row in await memory.list_sessions(workspace=tmp_path)}
|
||||
|
||||
task = asyncio.ensure_future(state.compact_to_new_session())
|
||||
await asyncio.wait_for(client.entered.wait(), timeout=5)
|
||||
_ = task.cancel()
|
||||
with pytest.raises(ValueError, match="compaction cancelled"):
|
||||
await task
|
||||
|
||||
assert state.session_id == old_id
|
||||
after = {row.sid for row in await memory.list_sessions(workspace=tmp_path)}
|
||||
assert after == before
|
||||
finally:
|
||||
await memory.close()
|
||||
|
||||
|
||||
async def test_rebuild_client_preserves_persist_cursor(tmp_path: Path) -> None:
|
||||
memory = await MemoryStore.open(DatabaseConfig())
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user