feat(briefing): add Message.msg_metadata and RssItem.topics/classified_at columns

msg_metadata maps to the 'metadata' DB column ('metadata' is reserved
by SQLAlchemy Declarative API). to_dict() exposes the key as 'metadata'
for frontend compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 10:30:37 -04:00
parent 8fa850534c
commit 5665f484ed
3 changed files with 33 additions and 1 deletions
@@ -67,6 +67,9 @@ class Message(Base, CreatedAtMixin):
Integer, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True Integer, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True
) )
tool_calls: Mapped[list | None] = mapped_column(JSONB, nullable=True) tool_calls: Mapped[list | None] = mapped_column(JSONB, nullable=True)
# 'metadata' is reserved by SQLAlchemy Declarative — use msg_metadata as the
# Python attribute name, mapped to the 'metadata' DB column.
msg_metadata: Mapped[dict | None] = mapped_column("metadata", JSONB, nullable=True)
conversation: Mapped["Conversation"] = relationship(back_populates="messages") conversation: Mapped["Conversation"] = relationship(back_populates="messages")
@@ -83,5 +86,6 @@ class Message(Base, CreatedAtMixin):
"status": self.status, "status": self.status,
"context_note_id": self.context_note_id, "context_note_id": self.context_note_id,
"tool_calls": self.tool_calls, "tool_calls": self.tool_calls,
"metadata": self.msg_metadata,
"created_at": self.created_at.isoformat(), "created_at": self.created_at.isoformat(),
} }
+9 -1
View File
@@ -1,6 +1,6 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text, UniqueConstraint from sqlalchemy import ARRAY, DateTime, ForeignKey, Index, Integer, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from fabledassistant.models import Base from fabledassistant.models import Base
@@ -49,6 +49,12 @@ class RssItem(Base):
fetched_at: Mapped[datetime] = mapped_column( fetched_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
) )
topics: Mapped[list[str]] = mapped_column(
ARRAY(Text), nullable=False, default=list, server_default="{}"
)
classified_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
feed: Mapped["RssFeed"] = relationship(back_populates="items") feed: Mapped["RssFeed"] = relationship(back_populates="items")
@@ -67,4 +73,6 @@ class RssItem(Base):
"url": self.url, "url": self.url,
"published_at": self.published_at.isoformat() if self.published_at else None, "published_at": self.published_at.isoformat() if self.published_at else None,
"content": self.content, "content": self.content,
"topics": self.topics or [],
"classified_at": self.classified_at.isoformat() if self.classified_at else None,
} }
+20
View File
@@ -18,3 +18,23 @@ def test_weather_cache_model_exists():
from fabledassistant.models.weather_cache import WeatherCache from fabledassistant.models.weather_cache import WeatherCache
cols = {c.key for c in WeatherCache.__table__.columns} cols = {c.key for c in WeatherCache.__table__.columns}
assert {"id", "user_id", "location_key", "location_label", "forecast_json", "previous_json", "fetched_at"} <= cols assert {"id", "user_id", "location_key", "location_label", "forecast_json", "previous_json", "fetched_at"} <= cols
def test_message_metadata_field_exists():
from fabledassistant.models.conversation import Message
# DB column is named 'metadata'; Python attribute is 'msg_metadata'
# (SQLAlchemy reserves 'metadata' as an attribute name on mapped classes)
col_names = {c.name for c in Message.__table__.columns}
assert "metadata" in col_names
m = Message(conversation_id=1, role="assistant", content="hi", msg_metadata={"foo": 1})
assert m.msg_metadata == {"foo": 1}
def test_rss_item_topics_field_exists():
from fabledassistant.models.rss_feed import RssItem
cols = {c.key for c in RssItem.__table__.columns}
assert "topics" in cols
assert "classified_at" in cols