Skip to content

feat: Phase 10B — save YouTube transcript to DB#20

Merged
Krasimir-Hristov merged 2 commits into
mainfrom
feature/phase-10b-save-youtube-transcript
May 10, 2026
Merged

feat: Phase 10B — save YouTube transcript to DB#20
Krasimir-Hristov merged 2 commits into
mainfrom
feature/phase-10b-save-youtube-transcript

Conversation

@Krasimir-Hristov

@Krasimir-Hristov Krasimir-Hristov commented May 10, 2026

Copy link
Copy Markdown
Owner
  • Add video_transcripts table (migration) with RLS
  • Add youtube/schemas.py, service.py, router.py, tool.py
  • Mount /api/v1/youtube REST endpoints in main.py
  • Add save_video_transcript handoff tool to supervisor
  • Wire save_transcript_node into orchestrator graph
  • Persist youtube_context in conversations.youtube_context
  • Load youtube_context at turn start so cross-turn save works
  • Emit tool_use SSE + Bookmark icon when saving

Summary by CodeRabbit

  • New Features
    • Save YouTube video summaries to your library for future reference
    • View your saved transcripts with full metadata including title, channel, duration, and key points
    • Manage saved content with easy deletion options

Review Change Stack

- Add video_transcripts table (migration) with RLS
- Add youtube/schemas.py, service.py, router.py, tool.py
- Mount /api/v1/youtube REST endpoints in main.py
- Add save_video_transcript handoff tool to supervisor
- Wire save_transcript_node into orchestrator graph
- Persist youtube_context in conversations.youtube_context
- Load youtube_context at turn start so cross-turn save works
- Emit tool_use SSE + Bookmark icon when saving
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Krasimir-Hristov has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 55 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8d923a5-af8e-48c4-93d5-81d3c461db87

📥 Commits

Reviewing files that changed from the base of the PR and between 5f98cef and 2787447.

📒 Files selected for processing (4)
  • backend/app/agents/supervisor.py
  • backend/app/features/chat/service.py
  • backend/app/features/youtube/router.py
  • backend/app/features/youtube/service.py

Walkthrough

This PR implements a complete YouTube transcript-saving feature. Users can now save video summaries generated by the YouTube agent; the supervisor prompts them to confirm, and when confirmed, a new save_transcript node persists the transcript to a database table with automatic cross-indexing into the memory system. REST endpoints allow listing, fetching, and deleting saved transcripts. The chat service manages youtube_context state across the conversation, and the frontend displays a bookmark icon during save operations.

Changes

YouTube Transcript Save Feature

Layer / File(s) Summary
Data Schema
supabase/migrations/20260510120000_create_video_transcripts.sql, backend/app/features/youtube/schemas.py
Creates video_transcripts table with user ownership, uniqueness on (user_id, video_id), RLS policies, and index. Defines VideoTranscriptOut Pydantic model with metadata, summary, and key_points.
Service Layer
backend/app/features/youtube/service.py
Implements CRUD functions: save_transcript inserts and cross-indexes into memory, list_transcripts fetches by user, get_transcript retrieves one or None, delete_transcript cleans up memory entries and deletes row.
Tool Node
backend/app/features/youtube/tool.py
save_transcript_node extracts tool_call_id, validates and parses youtube_context JSON, verifies required payload fields, calls service to persist, and returns closing ToolMessage.
Conversation State
backend/app/features/chat/conversation_service.py
Added get_conversation_youtube_context to load persisted state and update_conversation_youtube_context to persist updated context.
Chat Service Integration
backend/app/features/chat/service.py
stream_chat loads youtube_context at start, injects into initial state, captures updates from youtube_agent during streaming, emits "Saving to library…" when save_transcript node starts, and persists context in finally block.
Supervisor Agent
backend/app/agents/supervisor.py
Added SAVE_TRANSCRIPT_TOOL_NAME constant, updated system prompt with rules for prompting user to save and conditionally invoking save_video_transcript(), and bound new tool in model.
Orchestrator Graph
backend/app/agents/orchestrator.py
Imports save_transcript tool, extends _should_continue to route on SAVE_TRANSCRIPT_TOOL_NAME, registers save_transcript node, adds supervisor conditional edges, and loops save_transcript back to supervisor.
API Endpoints
backend/app/features/youtube/router.py
FastAPI router with authenticated endpoints: GET /youtube lists user's transcripts, GET /youtube/{id} fetches one (404 if not found), DELETE /youtube/{id} removes it (404 if not found).
App Setup
backend/app/main.py
Imports and mounts youtube_router at /api/v1 prefix.
Frontend UI
frontend/features/chat/components/ChatWindow.tsx
Added Bookmark icon import and updated toolStatus rendering to show Bookmark icon when status starts with "Saving".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Krasimir-Hristov/AXON#17: Introduces youtube_agent and youtube_context state that this PR extends with save functionality.
  • Krasimir-Hristov/AXON#19: Modifies supervisor routing and chat service YouTube handling that overlap with transcript save integration.
  • Krasimir-Hristov/AXON#18: Modifies supervisor tool bindings in the same orchestrator flow used here.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective: adding the ability to save YouTube transcripts to the database, which is reflected across the schema migration, service layer, REST endpoints, and orchestrator integration.
Docstring Coverage ✅ Passed Docstring coverage is 85.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/phase-10b-save-youtube-transcript

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/agents/supervisor.py`:
- Around line 122-136: The save rule is unsatisfiable because
conversation_service.load_history only restores "user" and "assistant" roles
(ToolMessage is not persisted) and the supervisor disables tools after
youtube_agent runs; change the prompt/check to look for the prior assistant
YouTube summary message in conversation history instead of requiring a
ToolMessage. Concretely, update the supervisor logic that enforces the YouTube
save rule so it searches recent assistant messages (the message content or a
marker produced by youtube_agent) to detect a YouTube summary, then allow
calling save_video_transcript() when the user explicitly confirms; keep
save_transcript_node reading state["youtube_context"] unchanged, and ensure the
supervisor pass that binds tools (where with_tools is set using memory_done)
will include save_video_transcript() when an assistant summary is present.
- Line 46: The local duplicate constant SAVE_TRANSCRIPT_TOOL_NAME in
supervisor.py must be removed and replaced by importing the canonical definition
from app/features/youtube/tool (where SAVE_TRANSCRIPT_TOOL_NAME is defined) so
both supervisor.py (used in the `@tool`(...) decorator in this file) and
orchestrator.py reference the same symbol; update supervisor.py to import
SAVE_TRANSCRIPT_TOOL_NAME from the youtube tool module and use that imported
constant in the `@tool`(...) decorator/registration to ensure a single source of
truth.

In `@backend/app/features/chat/service.py`:
- Around line 115-120: The code that reads youtube_context can crash if
event["data"]["output"] is None; update the guard in the astream_events handling
for kind == "on_chain_end" and node == "youtube_agent" to defensively coalesce
possible None values (e.g., use event.get("data") or {} and then .get("output")
or {} before accessing "youtube_context") so that pending_youtube_context is
only set when a string is present and the loop won't raise AttributeError.

In `@backend/app/features/youtube/router.py`:
- Around line 18-25: Add pagination to the list_transcripts_endpoint by
accepting limit and offset Query parameters (e.g., add Query import and params
to list_transcripts_endpoint signature) and pass them into
service.list_transcripts; then update service.list_transcripts to apply the
Supabase range call using range(offset, offset + limit - 1). Ensure
default/validation for limit/offset (reasonable default limit and non-negative
offset) and keep the same response model (list[VideoTranscriptOut]) while
returning only the requested page.

In `@backend/app/features/youtube/service.py`:
- Around line 110-124: list_transcripts currently returns an unbounded result
set which can cause full table scans; modify the function list_transcripts to
accept a limit (e.g. limit: int = 50) and optional keyset cursor (e.g.
before_created_at: str | None) and apply them to the Supabase query on table
"video_transcripts" (keep using _SELECT_COLS) by adding .limit(limit) and, for
keyset pagination, a .lt("created_at", before_created_at) when before_created_at
is provided and ensure the query still orders by created_at desc; update the
function signature and callers (router) to pass a default cap and optional
before cursor so the REST endpoint can page safely.
- Around line 168-189: The JSONB filter used in the memory_entries delete block
is incorrect: change the call that currently uses .eq("metadata->>video_id",
row.video_id) to use the supabase filter API — .filter("metadata->>video_id",
"eq", row.video_id) — so the JSONB arrow operator is handled correctly; update
the delete block in the delete_transcript section (the code using
client.table("memory_entries").delete(), mem_response, user_id and row.video_id)
and keep the surrounding try/except and logging intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c1409910-a68a-4521-9d97-6d90a824f9cf

📥 Commits

Reviewing files that changed from the base of the PR and between 6110a63 and 5f98cef.

📒 Files selected for processing (12)
  • backend/app/agents/orchestrator.py
  • backend/app/agents/supervisor.py
  • backend/app/features/chat/conversation_service.py
  • backend/app/features/chat/service.py
  • backend/app/features/youtube/__init__.py
  • backend/app/features/youtube/router.py
  • backend/app/features/youtube/schemas.py
  • backend/app/features/youtube/service.py
  • backend/app/features/youtube/tool.py
  • backend/app/main.py
  • frontend/features/chat/components/ChatWindow.tsx
  • supabase/migrations/20260510120000_create_video_transcripts.sql

Comment thread backend/app/agents/supervisor.py Outdated
Comment thread backend/app/agents/supervisor.py
Comment thread backend/app/features/chat/service.py
Comment thread backend/app/features/youtube/router.py Outdated
Comment thread backend/app/features/youtube/service.py Outdated
Comment thread backend/app/features/youtube/service.py
- supervisor.py: remove duplicate SAVE_TRANSCRIPT_TOOL_NAME, import from tool.py
- supervisor.py: reword save prompt to reference assistant summary (not ToolMessage)
- chat/service.py: defensive None coalescing for on_chain_end output
- youtube/service.py: add limit/offset pagination to list_transcripts
- youtube/service.py: fix JSONB filter .eq() -> .filter() for metadata->>video_id
- youtube/router.py: expose limit/offset Query params on list endpoint
@Krasimir-Hristov Krasimir-Hristov merged commit 47e69e2 into main May 10, 2026
2 checks passed
@Krasimir-Hristov Krasimir-Hristov deleted the feature/phase-10b-save-youtube-transcript branch May 10, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant