feat: briefing conversation management API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 20:07:13 -04:00
parent ebbf1a91f2
commit b66e6e0ad5
2 changed files with 142 additions and 0 deletions
+68
View File
@@ -9,9 +9,15 @@ from sqlalchemy import select
from fabledassistant.auth import login_required
from fabledassistant.models import async_session
from fabledassistant.models.conversation import Conversation, Message
from fabledassistant.models.rss_feed import RssFeed
from fabledassistant.services import rss as rss_svc
from fabledassistant.services import weather as weather_svc
from fabledassistant.services.briefing_conversations import (
get_or_create_today_conversation,
list_briefing_conversations,
post_message,
)
from fabledassistant.services.settings import get_setting, set_settings_batch
logger = logging.getLogger(__name__)
@@ -164,3 +170,65 @@ async def refresh_weather():
logger.warning("Failed to refresh weather for %s", key, exc_info=True)
return jsonify({"refreshed": refreshed})
# ── Briefing Conversations ─────────────────────────────────────────────────────
@briefing_bp.route("/conversations", methods=["GET"])
@_REQUIRE
async def get_conversations():
convs = await list_briefing_conversations(g.user.id)
return jsonify({"conversations": convs})
@briefing_bp.route("/conversations/today", methods=["GET"])
@_REQUIRE
async def get_today_conversation():
model = await get_setting(g.user.id, "default_model", "")
conv = await get_or_create_today_conversation(g.user.id, model)
# Load messages
async with async_session() as session:
result = await session.execute(
select(Message)
.where(Message.conversation_id == conv.id)
.order_by(Message.created_at)
)
messages = [m.to_dict() for m in result.scalars().all()]
data = conv.to_dict()
data["messages"] = messages
return jsonify(data)
@briefing_bp.route("/conversations/<int:conv_id>/messages", methods=["GET"])
@_REQUIRE
async def get_conversation_messages(conv_id: int):
# Verify ownership
async with async_session() as session:
conv = await session.get(Conversation, conv_id)
if not conv or conv.user_id != g.user.id or conv.conversation_type != "briefing":
return jsonify({"error": "Not found"}), 404
result = await session.execute(
select(Message)
.where(Message.conversation_id == conv_id)
.order_by(Message.created_at)
)
messages = [m.to_dict() for m in result.scalars().all()]
return jsonify({"messages": messages})
@briefing_bp.route("/trigger", methods=["POST"])
@_REQUIRE
async def manual_trigger():
"""Dev/admin endpoint to manually trigger a briefing compilation."""
data = await request.get_json() or {}
slot = data.get("slot", "compilation")
if slot not in ("compilation", "morning", "midday", "afternoon"):
return jsonify({"error": "invalid slot"}), 400
from fabledassistant.services.briefing_pipeline import run_compilation
model = await get_setting(g.user.id, "default_model", "")
conv = await get_or_create_today_conversation(g.user.id, model)
text = await run_compilation(g.user.id, slot, model)
msg = await post_message(conv.id, "assistant", text)
return jsonify({"conversation_id": conv.id, "message_id": msg.id, "slot": slot})