mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-20 19:46:16 +00:00
Hexagonal inner ring for the feedback bounded context: frozen aggregate with construct-time invariants (rating, reason-tag slugs), technology- neutral ports (typing.Protocol), and the application service with explicit user_id. Guarded by an AST purity test that keeps domain/ free of infrastructure imports.
70 lines
2.7 KiB
Python
70 lines
2.7 KiB
Python
from typing import Protocol, runtime_checkable
|
|
|
|
from deerflow.domain.feedback.model import Feedback
|
|
|
|
|
|
@runtime_checkable
|
|
class FeedbackRepository(Protocol):
|
|
"""Output port for feedback persistence.
|
|
|
|
Implementations exchange domain objects only and translate storage
|
|
failures into domain errors -- no storage vocabulary (SQL, tables,
|
|
files) may leak through this contract.
|
|
"""
|
|
|
|
async def save(self, feedback: Feedback) -> Feedback:
|
|
"""Store the user's current rating for a run (idempotent upsert).
|
|
|
|
Creates the entry if absent; otherwise replaces rating/comment and
|
|
refreshes created_at while keeping the aggregate identity
|
|
(thread_id, run_id, user_id). Returns the stored state.
|
|
"""
|
|
...
|
|
|
|
async def latest_per_run_in_thread(self, thread_id: str, *, user_id: str | None) -> dict[str, Feedback]:
|
|
"""Return each run's current feedback across a whole thread, keyed by run_id.
|
|
|
|
Single bulk read used to badge the message list -- avoids one
|
|
query per run. Ownership filter: a non-None user_id restricts results to the
|
|
user's entries; None means no filtering (no-auth mode).
|
|
"""
|
|
...
|
|
|
|
async def latest_for_runs(self, thread_id: str, run_ids: set[str], *, user_id: str | None) -> dict[str, Feedback]:
|
|
"""Return current feedback for only the selected runs of a thread.
|
|
|
|
Paged variant of latest_per_run_in_thread: the message-list page
|
|
endpoint only needs badges for the runs on the current page.
|
|
Returns an empty mapping when run_ids is empty. Same ownership
|
|
filter as find_for_run.
|
|
"""
|
|
...
|
|
|
|
async def remove_for_run(self, thread_id: str, run_id: str, *, user_id: str | None) -> bool:
|
|
"""Retract the user's feedback for a run.
|
|
|
|
Returns True if an entry was removed, False if none existed.
|
|
Ownership filter: a non-None user_id restricts results to the
|
|
user's entries; None means no filtering (no-auth mode).
|
|
"""
|
|
...
|
|
|
|
|
|
class RunLookup(Protocol):
|
|
"""Narrow output port: the only question the feedback context asks
|
|
about runs -- which thread does a run belong to.
|
|
|
|
Deliberately not the full run store: depending on this one-method
|
|
contract keeps the feedback context decoupled from the execution
|
|
context's wide repository interface (interface segregation).
|
|
"""
|
|
|
|
async def thread_of(self, run_id: str) -> str | None:
|
|
"""Return the thread_id owning the given run, or None if the run
|
|
does not exist.
|
|
|
|
Used by the service to verify run ownership before writing
|
|
feedback (rejects cross-thread run ids as RunNotFoundError).
|
|
"""
|
|
...
|