Files
Artemis-Eapil/artemis/exploration/demonstrations.py
T

126 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Durable manual-demonstration commands, sharing the automation transaction."""
import time
from uuid import uuid4
from artemis.exploration.store import ExplorationConflict, ExplorationStore
TERMINAL = {"completed", "failed", "cancelled", "timed_out"}
class Demonstrations:
def __init__(self, repository: ExplorationStore):
self.repository = repository
def get(self, demo_id: str) -> dict:
value = self.repository.database.get("exploration_demo", demo_id)
if value is None:
raise KeyError("人工示范不存在")
return value
def list(self, session_id: str) -> list[dict]:
self.repository.get(session_id)
with self.repository.database.connection() as connection:
rows = connection.execute(
"SELECT payload FROM automation_records WHERE kind='exploration_demo' "
"AND json_extract(payload,'$.exploration_id')=? ORDER BY updated_at",
(session_id,),
).fetchall()
import json
return [json.loads(row[0]) for row in rows]
def request(self, session_id: str, source: str, request_id: str) -> dict:
repo = self.repository
with repo._transaction() as connection:
session = repo._require(connection, session_id)
def create():
run_id = session["active_run_id"]
run = repo._get(connection, "exploration_run", run_id) if run_id else None
if (
not run
or run["kind"] != "exploration"
or run["status"] not in {"running", "waiting_user"}
):
raise ExplorationConflict("请等待探索开始执行,再请求人工示范")
if session["active_demonstration_id"]:
raise ExplorationConflict("已有进行中的人工示范")
value = {
"id": str(uuid4()),
"exploration_id": session_id,
"run_id": run_id,
"source": source,
"status": "requested",
"command": None,
"created_at": time.time(),
"deadline": None,
"media": [],
"error": None,
"timeout_seconds": session["config"]["demonstration_timeout_seconds"],
"warning_seconds": session["config"]["demonstration_warning_seconds"],
}
repo._put(connection, "exploration_demo", value["id"], value)
session["active_demonstration_id"] = value["id"]
repo._save_session(connection, session)
return value
return repo._operation(connection, f"{session_id}:demo", request_id, source, create)
def command(
self, session_id: str, demo_id: str, command: str, request_id: str, *, seconds: int = 0
) -> dict:
if command not in {"finish", "cancel", "extend"}:
raise ValueError("不支持的示范操作")
if command == "extend" and not 60 <= seconds <= 86400:
raise ValueError("延长时长必须在 60–86400 秒之间")
repo = self.repository
with repo._transaction() as connection:
session = repo._require(connection, session_id)
demo = repo._get(connection, "exploration_demo", demo_id)
if not demo or demo["exploration_id"] != session_id:
raise KeyError("人工示范不存在")
def update():
if session["active_demonstration_id"] != demo_id or demo["status"] not in {
"requested",
"recording",
}:
raise ExplorationConflict("示范已结束或正在分析")
if command != "cancel" and demo["status"] != "recording":
raise ExplorationConflict("请等待录像就绪")
if demo.get("command"):
raise ExplorationConflict("示范已请求结束")
if demo.get("deadline") and time.time() >= demo["deadline"]:
raise ExplorationConflict("示范已到时,请等待保存录像")
if command == "extend":
demo["deadline"] += seconds
else:
demo["command"] = command
return repo._put(connection, "exploration_demo", demo_id, demo)
return repo._operation(
connection, f"{demo_id}:command", request_id, [command, seconds], update
)
def transition(self, demo_id: str, status: str, **fields) -> dict:
repo = self.repository
with repo._transaction() as connection:
demo = repo._get(connection, "exploration_demo", demo_id)
if not demo:
raise KeyError("人工示范不存在")
session = repo._require(connection, demo["exploration_id"])
if demo["status"] in TERMINAL:
return demo
if session["active_run_id"] != demo["run_id"]:
raise ExplorationConflict("示范所属执行已结束")
demo.update(status=status, **fields)
if status == "recording":
demo.update(started_at=time.time(), deadline=time.time() + demo["timeout_seconds"])
if status in TERMINAL:
demo["finished_at"] = time.time()
session["active_demonstration_id"] = None
repo._save_session(connection, session)
return repo._put(connection, "exploration_demo", demo_id, demo)