Files

53 lines
2.2 KiB
Python

"""ORM model for user feedback on assistant answers."""
from __future__ import annotations
from datetime import UTC, datetime
from sqlalchemy import Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from deerflow.persistence.base import Base
from deerflow.persistence.types import BeijingDateTime, PortableJSON
class FeedbackRow(Base):
__tablename__ = "feedback"
# ``target_key`` is a SHA-256 digest of user/thread/message/agent. A
# digest keeps the idempotency index well below MySQL's legacy utf8mb4
# key-size limit; indexing all four source strings would exceed it.
__table_args__ = (
Index("uq_feedback_target_key", "target_key", unique=True),
Index("ix_feedback_agent_created", "agent_id", "created_at"),
)
feedback_id: Mapped[str] = mapped_column(String(64), primary_key=True)
run_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
thread_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
user_id: Mapped[str | None] = mapped_column(String(64), index=True)
message_id: Mapped[str | None] = mapped_column(String(64))
# ``message_id`` is the LangChain/checkpoint message id serialized inside
# a RunEventStore ``llm.ai.response`` event. It stays nullable solely for
# legacy run-level feedback rows created before answer-level feedback.
agent_id: Mapped[str] = mapped_column(String(128), nullable=False, default="lead_agent")
target_key: Mapped[str] = mapped_column(String(64), nullable=False)
rating: Mapped[int] = mapped_column(nullable=False)
# +1 (thumbs-up) or -1 (thumbs-down)
comment: Mapped[str | None] = mapped_column(Text)
# Optional user detail. The API exposes this as ``detail`` and retains
# ``comment`` as a compatibility alias for older clients.
reasons: Mapped[list[str]] = mapped_column(PortableJSON(), default=list)
# Stable machine-readable reason codes for negative feedback.
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=lambda: datetime.now(UTC))
updated_at: Mapped[datetime] = mapped_column(
BeijingDateTime(),
default=lambda: datetime.now(UTC),
onupdate=lambda: datetime.now(UTC),
)