mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-26 21:39:43 +02:00
feat: add public report content retrieval and enhance report handling
This commit is contained in:
parent
59628fdf76
commit
e5626342fc
5 changed files with 192 additions and 43 deletions
|
|
@ -20,6 +20,7 @@ from app.services.public_chat_service import (
|
|||
clone_from_snapshot,
|
||||
get_public_chat,
|
||||
get_snapshot_podcast,
|
||||
get_snapshot_report,
|
||||
)
|
||||
from app.users import current_active_user
|
||||
|
||||
|
|
@ -114,3 +115,28 @@ async def stream_public_podcast(
|
|||
"Content-Disposition": f"inline; filename={os.path.basename(file_path)}",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{share_token}/reports/{report_id}/content")
|
||||
async def get_public_report_content(
|
||||
share_token: str,
|
||||
report_id: int,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""
|
||||
Get report content from a public chat snapshot.
|
||||
|
||||
No authentication required - the share_token provides access.
|
||||
Returns report content including title, markdown body, and metadata.
|
||||
"""
|
||||
report_info = await get_snapshot_report(session, share_token, report_id)
|
||||
|
||||
if not report_info:
|
||||
raise HTTPException(status_code=404, detail="Report not found")
|
||||
|
||||
return {
|
||||
"id": report_info.get("original_id"),
|
||||
"title": report_info.get("title"),
|
||||
"content": report_info.get("content"),
|
||||
"report_metadata": report_info.get("report_metadata"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ from app.db import (
|
|||
Podcast,
|
||||
PodcastStatus,
|
||||
PublicChatSnapshot,
|
||||
Report,
|
||||
SearchSpaceMembership,
|
||||
User,
|
||||
)
|
||||
|
|
@ -38,6 +39,7 @@ UI_TOOLS = {
|
|||
"display_image",
|
||||
"link_preview",
|
||||
"generate_podcast",
|
||||
"generate_report",
|
||||
"scrape_webpage",
|
||||
"multi_link_preview",
|
||||
}
|
||||
|
|
@ -195,19 +197,22 @@ async def create_snapshot(
|
|||
message_ids = []
|
||||
podcasts_data = []
|
||||
podcast_ids_seen: set[int] = set()
|
||||
reports_data = []
|
||||
report_ids_seen: set[int] = set()
|
||||
|
||||
for msg in sorted(thread.messages, key=lambda m: m.created_at):
|
||||
author = await get_author_display(session, msg.author_id, user_cache)
|
||||
sanitized_content = sanitize_content_for_public(msg.content)
|
||||
|
||||
# Extract podcast references and update status to "ready" for completed podcasts
|
||||
# Extract podcast/report references and update status to "ready" for completed ones
|
||||
if isinstance(sanitized_content, list):
|
||||
for part in sanitized_content:
|
||||
if (
|
||||
isinstance(part, dict)
|
||||
and part.get("type") == "tool-call"
|
||||
and part.get("toolName") == "generate_podcast"
|
||||
):
|
||||
if not isinstance(part, dict) or part.get("type") != "tool-call":
|
||||
continue
|
||||
|
||||
tool_name = part.get("toolName")
|
||||
|
||||
if tool_name == "generate_podcast":
|
||||
result_data = part.get("result", {})
|
||||
podcast_id = result_data.get("podcast_id")
|
||||
if podcast_id and podcast_id not in podcast_ids_seen:
|
||||
|
|
@ -220,6 +225,19 @@ async def create_snapshot(
|
|||
# Update status to "ready" so frontend renders PodcastPlayer
|
||||
part["result"] = {**result_data, "status": "ready"}
|
||||
|
||||
elif tool_name == "generate_report":
|
||||
result_data = part.get("result", {})
|
||||
report_id = result_data.get("report_id")
|
||||
if report_id and report_id not in report_ids_seen:
|
||||
report_info = await _get_report_for_snapshot(
|
||||
session, report_id
|
||||
)
|
||||
if report_info:
|
||||
reports_data.append(report_info)
|
||||
report_ids_seen.add(report_id)
|
||||
# Update status to "ready" so frontend renders ReportCard
|
||||
part["result"] = {**result_data, "status": "ready"}
|
||||
|
||||
messages_data.append(
|
||||
{
|
||||
"id": msg.id,
|
||||
|
|
@ -266,6 +284,7 @@ async def create_snapshot(
|
|||
"author": thread_author,
|
||||
"messages": messages_data,
|
||||
"podcasts": podcasts_data,
|
||||
"reports": reports_data,
|
||||
}
|
||||
|
||||
# Create new snapshot
|
||||
|
|
@ -309,6 +328,25 @@ async def _get_podcast_for_snapshot(
|
|||
}
|
||||
|
||||
|
||||
async def _get_report_for_snapshot(
|
||||
session: AsyncSession,
|
||||
report_id: int,
|
||||
) -> dict | None:
|
||||
"""Get report info for embedding in snapshot_data."""
|
||||
result = await session.execute(select(Report).filter(Report.id == report_id))
|
||||
report = result.scalars().first()
|
||||
|
||||
if not report:
|
||||
return None
|
||||
|
||||
return {
|
||||
"original_id": report.id,
|
||||
"title": report.title,
|
||||
"content": report.content,
|
||||
"report_metadata": report.report_metadata,
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Snapshot Retrieval
|
||||
# =============================================================================
|
||||
|
|
@ -578,6 +616,7 @@ async def clone_from_snapshot(
|
|||
data = snapshot.snapshot_data
|
||||
messages_data = data.get("messages", [])
|
||||
podcasts_lookup = {p.get("original_id"): p for p in data.get("podcasts", [])}
|
||||
reports_lookup = {r.get("original_id"): r for r in data.get("reports", [])}
|
||||
|
||||
new_thread = NewChatThread(
|
||||
title=data.get("title", "Cloned Chat"),
|
||||
|
|
@ -594,6 +633,7 @@ async def clone_from_snapshot(
|
|||
await session.flush()
|
||||
|
||||
podcast_id_mapping: dict[int, int] = {}
|
||||
report_id_mapping: dict[int, int] = {}
|
||||
|
||||
# Check which authors from snapshot still exist in DB
|
||||
author_ids_from_snapshot: set[UUID] = set()
|
||||
|
|
@ -655,6 +695,34 @@ async def clone_from_snapshot(
|
|||
"podcast_id": podcast_id_mapping[old_podcast_id],
|
||||
}
|
||||
|
||||
if (
|
||||
isinstance(part, dict)
|
||||
and part.get("type") == "tool-call"
|
||||
and part.get("toolName") == "generate_report"
|
||||
):
|
||||
result = part.get("result", {})
|
||||
old_report_id = result.get("report_id")
|
||||
|
||||
if old_report_id and old_report_id not in report_id_mapping:
|
||||
report_info = reports_lookup.get(old_report_id)
|
||||
if report_info:
|
||||
new_report = Report(
|
||||
title=report_info.get("title", "Cloned Report"),
|
||||
content=report_info.get("content"),
|
||||
report_metadata=report_info.get("report_metadata"),
|
||||
search_space_id=target_search_space_id,
|
||||
thread_id=new_thread.id,
|
||||
)
|
||||
session.add(new_report)
|
||||
await session.flush()
|
||||
report_id_mapping[old_report_id] = new_report.id
|
||||
|
||||
if old_report_id and old_report_id in report_id_mapping:
|
||||
part["result"] = {
|
||||
**result,
|
||||
"report_id": report_id_mapping[old_report_id],
|
||||
}
|
||||
|
||||
new_message = NewChatMessage(
|
||||
thread_id=new_thread.id,
|
||||
role=role,
|
||||
|
|
@ -696,3 +764,29 @@ async def get_snapshot_podcast(
|
|||
return podcast
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def get_snapshot_report(
|
||||
session: AsyncSession,
|
||||
share_token: str,
|
||||
report_id: int,
|
||||
) -> dict | None:
|
||||
"""
|
||||
Get report info from a snapshot by original report ID.
|
||||
|
||||
Used for displaying report content in public view.
|
||||
Looks up the report by its original_id in the snapshot's reports array.
|
||||
"""
|
||||
snapshot = await get_snapshot_by_token(session, share_token)
|
||||
|
||||
if not snapshot:
|
||||
return None
|
||||
|
||||
reports = snapshot.snapshot_data.get("reports", [])
|
||||
|
||||
# Find report by original_id
|
||||
for report in reports:
|
||||
if report.get("original_id") == report_id:
|
||||
return report
|
||||
|
||||
return None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue