feat: release 1.1.0 with responsive task control

This commit is contained in:
chenmingxuan committed 2026-09-24 03:31:17 +08:00
1 parent 057f0c3050
commit bfcd828fa8
37 files changed
+1601 -151

No files matched your search

+98
View File
@@ -52,3 +52,101 @@ async def test_pause_releases_device_and_resume_waits_for_new_acquisition(tmp_pa
async def _event(name, events):
events.append(name)
@pytest.mark.asyncio
async def test_pause_interrupts_long_helper_and_discards_stale_result(tmp_path, monkeypatch):
monkeypatch.setattr(task_control, "TRACES_PATH", tmp_path)
events = []
started = asyncio.Event()
class Lock:
def __init__(self, device_id, **kwargs):
self.device_id = device_id
self.description = "test"
self.max_concurrency = None
self.concurrency_mode = "per_device"
self.ingress = "web"
self.lock_scope = "test"
def release(self):
events.append("released")
def acquire(self, cancel_event=None):
events.append("acquired")
monkeypatch.setattr(task_control, "DeviceExecutionLock", Lock)
control = task_control.TaskControl("task-123", Lock("device"))
async def diagnostic():
started.set()
try:
await asyncio.Event().wait()
finally:
events.append("diagnostic_stopped")
result = asyncio.create_task(
control.run_interruptibly(
diagnostic(),
on_pause=lambda: _event("capture_stopped", events),
on_resume=lambda: _event("capture_started", events),
)
)
await asyncio.wait_for(started.wait(), 1)
task_control.request_pause("task-123")
for _ in range(30):
if task_control.read_state("task-123").get("status") == "task_paused":
break
await asyncio.sleep(0.01)
assert task_control.read_state("task-123")["status"] == "task_paused"
assert events == ["diagnostic_stopped", "capture_stopped", "released"]
task_control.request_resume("task-123")
assert await asyncio.wait_for(result, 2) == (True, None)
assert events[-2:] == ["acquired", "capture_started"]
@pytest.mark.asyncio
async def test_interruptible_call_returns_normal_result_without_pause(tmp_path, monkeypatch):
monkeypatch.setattr(task_control, "TRACES_PATH", tmp_path)
control = task_control.TaskControl("task-123", object())
async def model_call():
return "current result"
assert await control.run_interruptibly(model_call()) == (False, "current result")
def test_guidance_history_distinguishes_submission_from_runner_consumption(tmp_path, monkeypatch):
monkeypatch.setattr(task_control, "TRACES_PATH", tmp_path)
first = task_control.add_instruction("task-123", "从快捷设置进入", source="web")
second = task_control.add_instruction("task-123", "不要更改设置", source="mcp")
assert [item["status"] for item in task_control.list_instructions("task-123")] == [
"pending",
"pending",
]
consumed = task_control.consume_instructions(task_control.control_dir("task-123"))
assert consumed["instruction"] == "从快捷设置进入\n\n不要更改设置"
assert task_control.consume_instructions(task_control.control_dir("task-123")) is None
history = task_control.list_instructions("task-123")
assert [item["id"] for item in history] == [first["id"], second["id"]]
assert all(item["status"] == "consumed" and item["consumed_at"] for item in history)
def test_guidance_history_endpoint_returns_pending_and_consumed(tmp_path, monkeypatch):
from fastapi import FastAPI
from fastapi.testclient import TestClient
from apps.admin_console.routers.tasks import router
monkeypatch.setattr(task_control, "TRACES_PATH", tmp_path)
task_control.add_instruction("task-123", "从快捷设置进入")
app = FastAPI()
app.include_router(router)
client = TestClient(app)
response = client.get("/api/tasks/task-123/guidance")
assert response.status_code == 200
assert response.json()[0]["status"] == "pending"
task_control.consume_instructions(task_control.control_dir("task-123"))
response = client.get("/api/tasks/task-123/guidance")
assert response.json()[0]["status"] == "consumed"