feat(injects,conversations,scheduler,gateway_tools,api): wake priority, master and parent aliases, scheduled injects start a turn
This commit is contained in:
@@ -103,6 +103,9 @@ __all__ = [
|
||||
|
||||
_log = logging.getLogger("beaver_gateway.core.conversations")
|
||||
|
||||
MASTER_ALIAS = "master"
|
||||
PARENT_ALIAS = "parent"
|
||||
|
||||
SEEDS = ("clean", "morning", "copy", "brief")
|
||||
_STATUSES = ("open", "merged", "closed", "archived")
|
||||
_DEFAULT_MERGE_PROMPT = (
|
||||
@@ -342,6 +345,23 @@ class Conversations:
|
||||
async with self._db.session() as session:
|
||||
return await session.get(Conversation, row_id)
|
||||
|
||||
async def resolve(
|
||||
self, key: str, *, origin: Conversation | None = None
|
||||
) -> Conversation | None:
|
||||
"""A conversation by public id or alias.
|
||||
|
||||
``master`` is the open master, ``parent`` the parent of ``origin`` -
|
||||
the names a job or a branch can use without knowing today's ids.
|
||||
"""
|
||||
key = key.strip()
|
||||
if key == MASTER_ALIAS:
|
||||
return await self._open_master()
|
||||
if key == PARENT_ALIAS:
|
||||
if origin is None or origin.parent_id is None:
|
||||
return None
|
||||
return await self.get_row(origin.parent_id)
|
||||
return await self.get(key)
|
||||
|
||||
async def find(
|
||||
self,
|
||||
*,
|
||||
@@ -596,7 +616,7 @@ class Conversations:
|
||||
if live is not None and live.busy:
|
||||
return True
|
||||
pending = await self._queue.pending(cast("int", row.id))
|
||||
return any(i.priority in ("user", "urgent") for i in pending)
|
||||
return any(i.priority in ("user", "urgent", "wake") for i in pending)
|
||||
|
||||
# ---- routing -------------------------------------------------------
|
||||
|
||||
@@ -841,6 +861,22 @@ class Conversations:
|
||||
origin: str = "system",
|
||||
interrupt: bool = True,
|
||||
) -> InjectQueueItem:
|
||||
if conv.kind == "master" and conv.status != "open":
|
||||
live = await self._open_master()
|
||||
if live is None:
|
||||
_log.error(
|
||||
"inject (%s) for closed master %s: no open master, it stays there",
|
||||
origin,
|
||||
conv.external_id,
|
||||
)
|
||||
else:
|
||||
_log.info(
|
||||
"inject (%s) for closed master %s goes to %s",
|
||||
origin,
|
||||
conv.external_id,
|
||||
live.external_id,
|
||||
)
|
||||
conv = live
|
||||
item = await self._queue.push(
|
||||
conversation_id=cast("int", conv.id),
|
||||
priority=urgency,
|
||||
@@ -923,12 +959,20 @@ class Conversations:
|
||||
return result
|
||||
|
||||
async def schedule(
|
||||
self, conv: Conversation, at: str, text: str, *, dedupe_key: str | None = None
|
||||
self,
|
||||
conv: Conversation,
|
||||
at: str,
|
||||
text: str,
|
||||
*,
|
||||
urgency: Priority = "wake",
|
||||
dedupe_key: str | None = None,
|
||||
) -> tuple[int | None, datetime]:
|
||||
if self.scheduler is None:
|
||||
msg = "no scheduler; `schedule` is unavailable"
|
||||
raise RuntimeError(msg)
|
||||
return await self.scheduler.schedule(conv, at, text, dedupe_key=dedupe_key)
|
||||
return await self.scheduler.schedule(
|
||||
conv, at, text, urgency=urgency, dedupe_key=dedupe_key
|
||||
)
|
||||
|
||||
async def schedules(self, conv: Conversation | None = None) -> list[dict[str, Any]]:
|
||||
return await self.scheduler.scheduled(conv) if self.scheduler else []
|
||||
@@ -1632,15 +1676,15 @@ class Conversations:
|
||||
) -> tuple[list[InjectQueueItem] | None, float | None]:
|
||||
if not items:
|
||||
return None, None
|
||||
normals = [i for i in items if i.priority == "normal"]
|
||||
head = items[0]
|
||||
if head.priority == "urgent":
|
||||
return [head], None
|
||||
if head.priority == "user":
|
||||
return [head, *normals], None
|
||||
tail = [i for i in items if i is not head and i.priority in ("wake", "normal")]
|
||||
if head.priority in ("user", "wake"):
|
||||
return [head, *tail], None
|
||||
age = (datetime.now(UTC) - _aware(head.created_at)).total_seconds()
|
||||
if age >= self._normal_window:
|
||||
return normals, None
|
||||
return [head, *tail], None
|
||||
return None, max(self._normal_window - age, 1.0)
|
||||
|
||||
async def _run_batch(
|
||||
|
||||
Reference in New Issue
Block a user