refactor: split flat core into capability packages, layer the conversations service, English defaults for every model-facing text
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""Putting words into a conversation: a message, an inject, ``say``, ``schedule``."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from beaver_gateway.conversations.turns import Turns
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
||||
from beaver_gateway.conversations.injects import Priority
|
||||
from beaver_gateway.storage.models import Conversation, InjectQueueItem
|
||||
|
||||
__all__ = ["Messaging"]
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Messaging(Turns):
|
||||
async def post(
|
||||
self, conv: Conversation, text: str, *, origin: str = "user"
|
||||
) -> InjectQueueItem:
|
||||
item = await self._queue.push(
|
||||
conversation_id=cast("int", conv.id),
|
||||
priority="user",
|
||||
origin=origin,
|
||||
text=text,
|
||||
)
|
||||
await self.touch_user(conv)
|
||||
self._bus.publish(
|
||||
"message.queued",
|
||||
conversation_id=conv.external_id,
|
||||
item=item.id,
|
||||
origin=origin,
|
||||
)
|
||||
self._ensure_worker(cast("int", conv.id))
|
||||
return item
|
||||
|
||||
async def inject(
|
||||
self,
|
||||
conv: Conversation,
|
||||
text: str,
|
||||
*,
|
||||
urgency: Priority = "normal",
|
||||
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,
|
||||
origin=origin,
|
||||
text=text,
|
||||
)
|
||||
self._bus.publish(
|
||||
"inject.queued",
|
||||
conversation_id=conv.external_id,
|
||||
item=item.id,
|
||||
priority=urgency,
|
||||
origin=origin,
|
||||
)
|
||||
if urgency == "urgent" and interrupt:
|
||||
backend = self._backend(conv.agent_name)
|
||||
if await backend.interrupt(conv.external_id):
|
||||
_log.info(
|
||||
"conversation %s: interrupted for urgent inject", conv.external_id
|
||||
)
|
||||
await self._queue.mark_interrupting(item)
|
||||
self._ensure_worker(cast("int", conv.id))
|
||||
return item
|
||||
|
||||
async def say(self, conv: Conversation, text: str) -> dict[str, Any]:
|
||||
runner = self._runners.get(cast("int", conv.id))
|
||||
_log.info("say[%s]: %s", conv.external_id, text[:200])
|
||||
return self._bus.publish(
|
||||
"say",
|
||||
conversation_id=conv.external_id,
|
||||
text=text,
|
||||
turn_id=runner.turn_id if runner is not None else None,
|
||||
)
|
||||
|
||||
async def schedule(
|
||||
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, 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 []
|
||||
Reference in New Issue
Block a user