feat(briefing): multi-article topic discussion endpoint for deep thematic analysis
This commit is contained in:
@@ -544,3 +544,106 @@ async def discuss_article(item_id: int):
|
|||||||
))
|
))
|
||||||
|
|
||||||
return jsonify({"assistant_message_id": assistant_msg.id, "status": "generating"}), 202
|
return jsonify({"assistant_message_id": assistant_msg.id, "status": "generating"}), 202
|
||||||
|
|
||||||
|
|
||||||
|
@briefing_bp.route("/topics/<topic>/discuss", methods=["POST"])
|
||||||
|
@_REQUIRE
|
||||||
|
async def discuss_topic(topic: str):
|
||||||
|
"""Discuss all recent articles in a topic cluster — multi-article deep analysis."""
|
||||||
|
data = await request.get_json() or {}
|
||||||
|
conv_id = data.get("conv_id")
|
||||||
|
if not conv_id:
|
||||||
|
return jsonify({"error": "conv_id required"}), 400
|
||||||
|
|
||||||
|
uid = g.user.id
|
||||||
|
|
||||||
|
# Verify conversation belongs to user
|
||||||
|
conv = await get_conversation(uid, conv_id)
|
||||||
|
if conv is None:
|
||||||
|
return jsonify({"error": "Conversation not found"}), 404
|
||||||
|
|
||||||
|
if get_buffer(conv_id) is not None:
|
||||||
|
return jsonify({"error": "Generation already in progress"}), 409
|
||||||
|
|
||||||
|
# Find recent articles with this topic (last 2 days)
|
||||||
|
async with async_session() as session:
|
||||||
|
result = await session.execute(
|
||||||
|
select(RssItem)
|
||||||
|
.join(RssFeed, RssFeed.id == RssItem.feed_id)
|
||||||
|
.where(RssFeed.user_id == uid)
|
||||||
|
.where(RssItem.topics.contains([topic]))
|
||||||
|
.order_by(RssItem.published_at.desc().nullslast())
|
||||||
|
.limit(5)
|
||||||
|
)
|
||||||
|
items = list(result.scalars().all())
|
||||||
|
|
||||||
|
if not items:
|
||||||
|
return jsonify({"error": f"No articles found for topic '{topic}'"}), 404
|
||||||
|
|
||||||
|
# Fetch full content for each article
|
||||||
|
from fabledassistant.services.rss import _fetch_full_article
|
||||||
|
|
||||||
|
synthetic_tool_calls = []
|
||||||
|
for item in items:
|
||||||
|
content = await _fetch_full_article(item.url) if item.url else None
|
||||||
|
content = content or item.content or ""
|
||||||
|
synthetic_tool_calls.append({
|
||||||
|
"function": "read_article",
|
||||||
|
"arguments": {"url": item.url or ""},
|
||||||
|
"result": {
|
||||||
|
"success": True,
|
||||||
|
"type": "article_content",
|
||||||
|
"url": item.url or "",
|
||||||
|
"title": item.title or "",
|
||||||
|
"source": "",
|
||||||
|
"content": content[:8000], # cap per article to stay within context
|
||||||
|
"truncated": len(content) > 8000,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await add_message(conv_id, "assistant", "", status="complete", tool_calls=synthetic_tool_calls)
|
||||||
|
|
||||||
|
user_prompt = (
|
||||||
|
f"I'd like to discuss the {len(items)} articles about '{topic}'. "
|
||||||
|
"Don't just summarize each one — draw connections between the sources, "
|
||||||
|
"highlight where they agree or disagree, and share your analysis of what "
|
||||||
|
"this means. Let's have a real discussion about this topic."
|
||||||
|
)
|
||||||
|
await add_message(conv_id, "user", user_prompt)
|
||||||
|
|
||||||
|
conv = await get_conversation(uid, conv_id)
|
||||||
|
assert conv is not None
|
||||||
|
|
||||||
|
history = []
|
||||||
|
for msg in conv.messages:
|
||||||
|
if msg.role == "system":
|
||||||
|
continue
|
||||||
|
msg_dict = {"role": msg.role, "content": msg.content or ""}
|
||||||
|
if msg.tool_calls:
|
||||||
|
msg_dict["tool_calls"] = [
|
||||||
|
{"function": {"name": tc["function"], "arguments": tc["arguments"]}}
|
||||||
|
for tc in msg.tool_calls
|
||||||
|
]
|
||||||
|
history.append(msg_dict)
|
||||||
|
for tc in msg.tool_calls:
|
||||||
|
history.append({"role": "tool", "content": json.dumps(tc.get("result", {}))})
|
||||||
|
else:
|
||||||
|
history.append(msg_dict)
|
||||||
|
|
||||||
|
model = await get_setting(uid, "default_model", "") or ""
|
||||||
|
|
||||||
|
assistant_msg = await add_message(conv_id, "assistant", "", status="generating")
|
||||||
|
buf = create_buffer(conv_id, assistant_msg.id)
|
||||||
|
|
||||||
|
asyncio.create_task(run_generation(
|
||||||
|
buf, history, model,
|
||||||
|
uid, conv_id, conv.title or "",
|
||||||
|
user_prompt,
|
||||||
|
think=True,
|
||||||
|
))
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"assistant_message_id": assistant_msg.id,
|
||||||
|
"status": "generating",
|
||||||
|
"article_count": len(items),
|
||||||
|
}), 202
|
||||||
|
|||||||
Reference in New Issue
Block a user