From 497dbe5a008440f49cc90f98a72f5ff074a994ad Mon Sep 17 00:00:00 2001 From: lda Date: Tue, 1 Sep 2026 01:51:43 +0700 Subject: [PATCH] feat: thin App facade over ops/store --- src/agentmsgs/core/__init__.py | 4 +++ src/agentmsgs/core/app.py | 63 ++++++++++++++++++++++++++++++++++ tests/test_app.py | 25 ++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/agentmsgs/core/__init__.py create mode 100644 src/agentmsgs/core/app.py create mode 100644 tests/test_app.py diff --git a/src/agentmsgs/core/__init__.py b/src/agentmsgs/core/__init__.py new file mode 100644 index 0000000..f6610f2 --- /dev/null +++ b/src/agentmsgs/core/__init__.py @@ -0,0 +1,4 @@ +from .app import App +from .types import Agent, Message, Thread + +__all__ = ["Agent", "Thread", "Message", "App"] diff --git a/src/agentmsgs/core/app.py b/src/agentmsgs/core/app.py new file mode 100644 index 0000000..f769eef --- /dev/null +++ b/src/agentmsgs/core/app.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import uuid +from dataclasses import dataclass, field + +from agentmsgs.stores.memory import InMemoryStore + +from . import ops +from .store import Store +from .types import Agent, Thread + + +@dataclass +class App: + store: Store = field(default_factory=InMemoryStore) + + def get_or_create_agent(self, name: str) -> Agent: + return ops.get_or_create_agent(self.store, name) + + def create_agent(self, name: str) -> Agent: + return ops.create_agent(self.store, name) + + def delete_account(self, id: uuid.UUID) -> None: + return ops.delete_agent(self.store, id) + + def create_thread(self, *agents: Agent) -> Thread: + return ops.create_thread(self.store, set(agents)) + + def get_thread(self, id: uuid.UUID) -> Thread | None: + return self.store.get_thread(id) + + def find_threads(self, *agents: Agent) -> list[Thread]: + return ops.find_threads(self.store, set(agents)) + + def find_thread(self, *agents: Agent) -> list[Thread]: + return self.find_threads(*agents) + + def join_thread(self, tid: uuid.UUID, agent: Agent) -> Thread: + return ops.join_thread(self.store, tid, agent) + + def leave_thread(self, tid: uuid.UUID, agent: Agent) -> Thread: + return ops.leave_thread(self.store, tid, agent) + + def append(self, tid: uuid.UUID, sender: Agent, content: str): + return ops.append_message(self.store, tid, sender, content) + + def poll(self, tid: uuid.UUID, agent: Agent, after_seq: int = 0): + return ops.list_messages(self.store, tid, after_seq) + + def mark_read(self, tid: uuid.UUID, agent: Agent, seq: int) -> None: + return ops.mark_read(self.store, tid, agent, seq) + + def has_unread(self, tid: uuid.UUID, agent: Agent, exclude_own: bool = False) -> bool: + return ops.has_unread(self.store, tid, agent, exclude_own) + + # compat shims for old tests + def add_thread(self, *a: Agent) -> Thread: + return self.create_thread(*a) + + def add_thread_2(self, thread: Thread): + if hasattr(self.store, "_threads"): + self.store._threads[thread.id] = thread # type: ignore[attr-defined] + return None diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..dec047c --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,25 @@ +from agentmsgs.core.app import App +from agentmsgs.stores.memory import InMemoryStore + + +def test_app_facade_with_memory(): + app = App(store=InMemoryStore()) + alice = app.get_or_create_agent("Alice") + bob = app.get_or_create_agent("Bob") + t = app.create_thread(alice, bob) + app.append(t.id, alice, "hello") + assert app.has_unread(t.id, bob) is True + msgs = app.poll(t.id, bob) + assert len(msgs) == 1 + app.mark_read(t.id, bob, msgs[0].seq) + assert app.has_unread(t.id, bob) is False + + +def test_app_delete_account(): + from agentmsgs.core.app import App + from agentmsgs.stores.memory import InMemoryStore + + app = App(store=InMemoryStore()) + a = app.get_or_create_agent("A") + app.delete_account(a.id) + assert app.store.get_agent_by_id(a.id) is None