mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-08-31 10:24:59 +00:00
knowledge_store: committer parameter on record and transaction
This commit is contained in:
parent
aa27c2fb47
commit
cca2b9480a
4 changed files with 42 additions and 9 deletions
|
|
@ -17,7 +17,10 @@ class Revision:
|
|||
"""One recorded point in a workspace's history (a whole-tree snapshot)."""
|
||||
|
||||
id: str
|
||||
#: Whose content change this is (the acting user).
|
||||
author: str
|
||||
#: Who recorded it (the agent for agent turns; equals author otherwise).
|
||||
committer: str
|
||||
message: str
|
||||
created_at: datetime
|
||||
|
||||
|
|
@ -64,10 +67,12 @@ class VersionedContentEngine(ABC):
|
|||
removes: Iterable[str],
|
||||
message: str,
|
||||
author: str,
|
||||
committer: str | None = None,
|
||||
) -> str | None:
|
||||
"""Append ``writes`` and ``removes`` to history as one revision.
|
||||
|
||||
Returns the revision id, or ``None`` when nothing changed.
|
||||
``committer`` defaults to ``author``. Returns the revision id, or
|
||||
``None`` when nothing changed.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ class GitContentEngine(VersionedContentEngine):
|
|||
removes: Iterable[str],
|
||||
message: str,
|
||||
author: str,
|
||||
committer: str | None = None,
|
||||
) -> str | None:
|
||||
self._ensure_exists()
|
||||
repo = Repo(str(self._path))
|
||||
|
|
@ -78,12 +79,11 @@ class GitContentEngine(VersionedContentEngine):
|
|||
if not self._has_pending_changes(repo):
|
||||
return None
|
||||
|
||||
author_bytes = author.encode()
|
||||
revision = porcelain.commit(
|
||||
repo,
|
||||
message=message.encode(),
|
||||
author=author_bytes,
|
||||
committer=author_bytes,
|
||||
author=author.encode(),
|
||||
committer=(committer or author).encode(),
|
||||
)
|
||||
return revision.decode()
|
||||
finally:
|
||||
|
|
@ -289,6 +289,7 @@ class GitContentEngine(VersionedContentEngine):
|
|||
return Revision(
|
||||
id=commit.id.decode(),
|
||||
author=commit.author.decode(),
|
||||
committer=commit.committer.decode(),
|
||||
message=commit.message.decode().strip(),
|
||||
created_at=datetime.fromtimestamp(commit.commit_time, tz=UTC),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,14 +38,19 @@ class KnowledgeStore:
|
|||
return cls(workspace_id, engine)
|
||||
|
||||
@asynccontextmanager
|
||||
async def transaction(self, *, message: str, author: str):
|
||||
async def transaction(
|
||||
self, *, message: str, author: str, committer: str | None = None
|
||||
):
|
||||
"""Atomic unit of work: verbs staged in the scope become one revision
|
||||
on clean exit; an exception records nothing."""
|
||||
on clean exit; an exception records nothing.
|
||||
|
||||
``author`` is whose content change this is; ``committer`` (default
|
||||
``author``) is who recorded it — the agent identity for agent turns."""
|
||||
tx = Transaction()
|
||||
yield tx
|
||||
async with workspace_write_lock(self._workspace_id):
|
||||
tx.revision = await asyncio.to_thread(
|
||||
self._record_revision, tx, message, author
|
||||
self._record_revision, tx, message, author, committer
|
||||
)
|
||||
|
||||
async def read_as_of(self, revision: str, path: str) -> bytes:
|
||||
|
|
@ -100,10 +105,14 @@ class KnowledgeStore:
|
|||
return self._engine.compute_content_id(data)
|
||||
|
||||
def _record_revision(
|
||||
self, tx: Transaction, message: str, author: str
|
||||
self, tx: Transaction, message: str, author: str, committer: str | None
|
||||
) -> str | None:
|
||||
"""Resolve the transaction into one change set and record it."""
|
||||
writes, removes = tx.resolve(self._engine.read)
|
||||
return self._engine.record(
|
||||
writes=writes, removes=removes, message=message, author=author
|
||||
writes=writes,
|
||||
removes=removes,
|
||||
message=message,
|
||||
author=author,
|
||||
committer=committer,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,24 @@ class TestHistoryQueries:
|
|||
assert rev.author == AUTHOR
|
||||
assert rev.created_at.tzinfo is not None
|
||||
|
||||
def test_revision_distinguishes_author_from_committer(self, engine):
|
||||
agent = "SurfSense Agent <agent@surfsense>"
|
||||
engine.record(
|
||||
writes={"a.xml": b"1"},
|
||||
removes=[],
|
||||
message="a1",
|
||||
author=AUTHOR,
|
||||
committer=agent,
|
||||
)
|
||||
rev = engine.list_revisions()[0]
|
||||
assert rev.author == AUTHOR
|
||||
assert rev.committer == agent
|
||||
|
||||
def test_committer_defaults_to_author(self, engine):
|
||||
engine.record(writes={"a.xml": b"1"}, removes=[], message="a1", author=AUTHOR)
|
||||
rev = engine.list_revisions()[0]
|
||||
assert rev.committer == AUTHOR
|
||||
|
||||
def test_list_changes_reports_kinds_and_content_ids(self, engine):
|
||||
first = engine.record(
|
||||
writes={"a.xml": b"a1", "b.xml": b"b1"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue