Skip to content

fix: YouTube transcript any-language + delete dialog + rename 500#19

Merged
Krasimir-Hristov merged 3 commits into
mainfrom
fix/youtube-transcript-and-delete-dialog
May 10, 2026
Merged

fix: YouTube transcript any-language + delete dialog + rename 500#19
Krasimir-Hristov merged 3 commits into
mainfrom
fix/youtube-transcript-and-delete-dialog

Conversation

@Krasimir-Hristov

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

Copy link
Copy Markdown
Owner

backend:

  • youtube_fetcher: use api.list() + find_transcript() to fetch transcript in any available language instead of defaulting to English only — fixes NoTranscriptFound for non-English videos
  • supervisor: add deterministic Python fast-path using extract_video_id() so YouTube URLs are always routed to youtube_agent without relying on LLM
  • conversation_service: two-query pattern for update_conversation_title — supabase-py v2 does not support .update().select() chaining (postgrest-py issue #394), causing 500 on rename
  • main: add global exception handler so unhandled 500s pass through CORSMiddleware — prevents browser from reporting them as 'Failed to fetch'
  • security: simplify redundant except tuple to bare except Exception
  • service: emit tool_use SSE event when youtube_agent node starts

frontend:

  • ConversationSidebar: replace 2-click confirm with proper delete dialog (single click → dialog with Cancel / Delete buttons)
  • ChatWindow: show Play icon (red) for YouTube tool, Database icon for memory
  • dialog.tsx: new Dialog UI component built on @base-ui/react

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced YouTube video handling with improved transcript fetching that prefers manual captions
    • Dialog-based confirmation modal for conversation deletion with clearer user feedback
    • YouTube transcript fetch status now displays with dedicated visual indicator and animation
  • Improvements

    • Faster detection and processing of YouTube video links
    • Better error handling and logging for improved system stability

Review Change Stack

backend:
- youtube_fetcher: use api.list() + find_transcript() to fetch transcript
  in any available language instead of defaulting to English only —
  fixes NoTranscriptFound for non-English videos
- supervisor: add deterministic Python fast-path using extract_video_id()
  so YouTube URLs are always routed to youtube_agent without relying on LLM
- conversation_service: two-query pattern for update_conversation_title —
  supabase-py v2 does not support .update().select() chaining (postgrest-py
  issue #394), causing 500 on rename
- main: add global exception handler so unhandled 500s pass through
  CORSMiddleware — prevents browser from reporting them as 'Failed to fetch'
- security: simplify redundant except tuple to bare except Exception
- service: emit tool_use SSE event when youtube_agent node starts

frontend:
- ConversationSidebar: replace 2-click confirm with proper delete dialog
  (single click → dialog with Cancel / Delete buttons)
- ChatWindow: show Play icon (red) for YouTube tool, Database icon for memory
- dialog.tsx: new Dialog UI component built on @base-ui/react
@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 37 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: 87d318cc-130e-4c7a-a00a-a9f82a9d85b4

📥 Commits

Reviewing files that changed from the base of the PR and between ddc9713 and 613ddd2.

📒 Files selected for processing (9)
  • COMMANDS.md
  • backend/app/agents/subagents/youtube_fetcher.py
  • backend/app/agents/supervisor.py
  • backend/app/features/chat/conversation_service.py
  • backend/app/features/chat/service.py
  • frontend/features/chat/components/ChatWindow.tsx
  • frontend/features/chat/components/ConversationRow.tsx
  • frontend/features/chat/components/ConversationSidebar.tsx
  • frontend/features/chat/components/SkeletonRow.tsx

Walkthrough

This PR enhances the chat agent system with YouTube video integration, improves conversation management UX via dialog modals, and strengthens infrastructure error handling. The supervisor now detects YouTube URLs in user messages and routes them directly to a dedicated YouTube agent without LLM involvement. Conversation deletion uses a dedicated modal dialog. Supabase query chaining limitations are worked around by splitting updates into separate operations. Global exception handling logs unhandled errors consistently.

Changes

YouTube Agent Integration

Layer / File(s) Summary
Transcript Selection Logic
backend/app/agents/subagents/youtube_fetcher.py
fetch_transcript() now lists available transcripts, prefers manual captions, and returns the first available transcript instead of directly fetching a default variant; docstring updated to reflect new behavior.
Supervisor Routing & Turn Detection
backend/app/agents/supervisor.py
Supervisor adds deterministic YouTube URL detection in last human message; when found and no sub-agent has run yet, emits transfer_to_youtube_agent tool call immediately, avoiding LLM-based routing; turn detection refactored from memory_done to general agent_ran scoped after the last HumanMessage; tool binding controlled via with_tools=not agent_ran to prevent re-delegation.
Stream Event Handling
backend/app/features/chat/service.py
Stream handler intercepts youtube_agent node start, clears buffered narration, forces tool-use flow, and emits tool_use SSE frame indicating YouTube transcript fetch.
Tool Status Indicator
frontend/features/chat/components/ChatWindow.tsx
Tool status UI conditionally displays Play icon with pulse when status indicates "Fetching YouTube"; otherwise displays Database icon.

Conversation Management & Dialog UX

Layer / File(s) Summary
Dialog UI Component
frontend/components/ui/dialog.tsx
New reusable dialog wrapper providing DialogRoot, DialogTrigger, DialogClose, DialogPopup, DialogTitle, DialogDescription primitives with Tailwind styling, overlay backdrop, entrance/exit animations, and ref forwarding.
Delete Confirmation Dialog
frontend/features/chat/components/ConversationSidebar.tsx
ConversationRow replaces inline "click twice" confirmation with modal delete confirmation dialog; dialog state controls visibility, delete action invokes mutation and closes on success, showing loading state during deletion.
Conversation Update Service
backend/app/features/chat/conversation_service.py
update_conversation_title refactored from chained .update().select() to separate update then select queries to work around postgrest-py limitations; returns None when update or follow-up select yields no data.

Infrastructure & Error Handling

Layer / File(s) Summary
Global Exception Handler
backend/app/main.py
FastAPI application-level handler for unhandled exceptions logs errors and returns consistent JSON 500 response with generic error message.
Security Logging
backend/app/core/security.py
JWT validation failure warning in get_current_user reformatted to multi-line logger call for improved readability; exception behavior unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Krasimir-Hristov/AXON#18: Overlapping changes across supervisor YouTube fast-path, youtube_fetcher transcript selection, exception handler, and conversation sidebar delete dialog.
  • Krasimir-Hristov/AXON#16: Directly related to the conversation_service.py update_conversation_title refactoring that splits chained queries into separate operations.
  • Krasimir-Hristov/AXON#4: Extends stream_chat implementation with youtube_agent node event interception and buffering adjustments in the same function.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 three main changes: YouTube transcript handling for any language, a new delete confirmation dialog, and a global 500 error handler.
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 fix/youtube-transcript-and-delete-dialog

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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/features/chat/components/ConversationSidebar.tsx (1)

250-306: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Split this file to satisfy the 300-line limit.

This file now reaches 306 lines (see Line 306). Please extract ConversationRow/SkeletonRow into separate feature component files to comply with project constraints.

As per coding guidelines, No file longer than 300 lines — split into smaller modules.

🤖 Prompt for 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.

In `@frontend/features/chat/components/ConversationSidebar.tsx` around lines 250 -
306, This file exceeds the 300-line limit; extract the ConversationRow and
SkeletonRow components out of ConversationSidebar into their own feature
component files (e.g., ConversationRow.tsx and SkeletonRow.tsx), keep
ConversationSidebar as the parent that imports these components, export the new
components as default or named exports, update the import statements in
ConversationSidebar to import ConversationRow and SkeletonRow, and ensure any
props types (e.g., ConversationRow's props and ConversationSidebarProps) are
exported or moved to a shared types file if needed so existing usage (onSelect,
onDeleted, isActive, conversation) continues to work.
🤖 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/subagents/youtube_fetcher.py`:
- Around line 71-83: Wrap the body of the _sync (or equivalent) function that
calls api.list(video_id) and
transcript_list.find_transcript(...).fetch().to_raw_data() in a try/except,
catch Exception as e, use the module logger (logging.getLogger(__name__)) to log
an error that includes the video_id and the exception (e) or use
logger.exception for traceback, then re-raise the exception so upstream handlers
still receive it; ensure both the api.list(video_id) call and the subsequent
transcript_list.find_transcript(...).fetch().to_raw_data() are inside the try
block so failures from either are logged with video_id context before being
re-raised.

In `@backend/app/agents/supervisor.py`:
- Around line 190-230: The fast-path currently calls extract_video_id(raw) which
misses common YouTube URL variants (e.g., m.youtube.com/watch, /live/, /embed/)
causing the branch to be skipped; update the logic used in supervisor.py by
either expanding extract_video_id to recognize these additional URL patterns
(including subdomains like m.youtube.com, paths /live/ and /embed/, and query
param forms) or pre-normalize raw before calling extract_video_id (parse any
URLs from last_human.content, normalize host to youtube.com, strip prefixes,
then pass the normalized URL or extracted path to extract_video_id) so the
YouTube handoff triggers for those variants. Ensure you reference
extract_video_id and the last_human/raw extraction flow when making the change.
- Around line 238-241: The model is being bound with transfer_to_youtube_agent
on first pass which reintroduces LLM-controlled YouTube routing; change the
binding logic so that when constructing the model for the first pass (where
agent_ran is False) you only bind transfer_to_memory_agent and explicitly
exclude transfer_to_youtube_agent (leave it unbound), and ensure
_get_model(model_id, ...) and its cache key are adjusted to depend on the actual
set of bound tools (not just the agent_ran boolean) so different tool-sets
produce distinct cached models.

In `@backend/app/features/chat/conversation_service.py`:
- Around line 246-262: The check that treats a successful Supabase update as
"not found" should be removed: delete the conditional that inspects
update_result.data (the "if not update_result.data: return None" block) in the
update_conversation_title flow so the function relies on the subsequent
select/fetch query instead of response.data; leave the update call, exception
handling (logger.exception), and the follow-up select logic intact so the real
existence/result is determined by the explicit select rather than the empty
update_result.data.

---

Outside diff comments:
In `@frontend/features/chat/components/ConversationSidebar.tsx`:
- Around line 250-306: This file exceeds the 300-line limit; extract the
ConversationRow and SkeletonRow components out of ConversationSidebar into their
own feature component files (e.g., ConversationRow.tsx and SkeletonRow.tsx),
keep ConversationSidebar as the parent that imports these components, export the
new components as default or named exports, update the import statements in
ConversationSidebar to import ConversationRow and SkeletonRow, and ensure any
props types (e.g., ConversationRow's props and ConversationSidebarProps) are
exported or moved to a shared types file if needed so existing usage (onSelect,
onDeleted, isActive, conversation) continues to work.
🪄 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: 1e6f8487-dedb-4fa4-a6a9-a08a9ee97073

📥 Commits

Reviewing files that changed from the base of the PR and between 4f41ed3 and ddc9713.

📒 Files selected for processing (9)
  • backend/app/agents/subagents/youtube_fetcher.py
  • backend/app/agents/supervisor.py
  • backend/app/core/security.py
  • backend/app/features/chat/conversation_service.py
  • backend/app/features/chat/service.py
  • backend/app/main.py
  • frontend/components/ui/dialog.tsx
  • frontend/features/chat/components/ChatWindow.tsx
  • frontend/features/chat/components/ConversationSidebar.tsx

Comment thread backend/app/agents/subagents/youtube_fetcher.py Outdated
Comment on lines +190 to +230
# -- Deterministic YouTube fast-path ------------------------------------
# Detect a YouTube URL in the last HumanMessage without asking the LLM.
# This avoids the failure mode where the model ignores the routing
# instruction and hallucinates a summary from its training data.
if not agent_ran:
last_human = next(
(m for m in reversed(messages) if isinstance(m, HumanMessage)),
None,
)
if last_human:
raw = (
last_human.content
if isinstance(last_human.content, str)
else " ".join(
p
if isinstance(p, str)
else p.get("text", "")
if isinstance(p, dict)
else getattr(p, "text", "")
for p in last_human.content
)
)
if extract_video_id(raw):
logger.info(
"[supervisor] YouTube URL detected — fast-path to youtube_agent"
)
return {
"messages": [
AIMessage(
content="",
tool_calls=[
{
"id": str(uuid4()),
"name": YOUTUBE_HANDOFF_TOOL_NAME,
"args": {},
"type": "tool_call",
}
],
)
]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The deterministic fast-path still misses common YouTube URL forms.

This branch only runs when extract_video_id(raw) matches. That helper currently recognizes watch?v=, youtu.be/, and /shorts/ URLs, so links such as m.youtube.com/watch?v=..., /live/..., or /embed/... still fall back to the LLM and reopen the hallucination path this fast-path is supposed to remove. Expand the extractor or normalize URLs before using it here.

🤖 Prompt for 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.

In `@backend/app/agents/supervisor.py` around lines 190 - 230, The fast-path
currently calls extract_video_id(raw) which misses common YouTube URL variants
(e.g., m.youtube.com/watch, /live/, /embed/) causing the branch to be skipped;
update the logic used in supervisor.py by either expanding extract_video_id to
recognize these additional URL patterns (including subdomains like
m.youtube.com, paths /live/ and /embed/, and query param forms) or pre-normalize
raw before calling extract_video_id (parse any URLs from last_human.content,
normalize host to youtube.com, strip prefixes, then pass the normalized URL or
extracted path to extract_video_id) so the YouTube handoff triggers for those
variants. Ensure you reference extract_video_id and the last_human/raw
extraction flow when making the change.

Comment thread backend/app/agents/supervisor.py Outdated
Comment thread backend/app/features/chat/conversation_service.py
backend:
- youtube_fetcher: wrap _sync body in try/except, log error with video_id
  before re-raising so failures from api.list() and find_transcript().fetch()
  are visible in logs with context
- youtube_fetcher: expand _YOUTUBE_URL_RE to match m.youtube.com, /live/,
  /embed/ paths in addition to existing watch?v=, youtu.be, /shorts/
- supervisor: rebind _get_model to accept tools list instead of bool; on
  first pass only bind transfer_to_memory_agent (exclude youtube tool so LLM
  can never hallucinate YouTube routing); cache key is now tuple of tool names
- conversation_service: remove early 'if not update_result.data: return None'
  after update query — supabase-py v2 returns empty data on successful updates;
  real existence is determined by the subsequent select query

frontend:
- ConversationSidebar: split SkeletonRow into SkeletonRow.tsx and
  ConversationRow into ConversationRow.tsx to satisfy 300-line limit;
  ConversationSidebar is now a lean 70-line orchestrator
@Krasimir-Hristov Krasimir-Hristov merged commit 6110a63 into main May 10, 2026
2 checks passed
@Krasimir-Hristov Krasimir-Hristov deleted the fix/youtube-transcript-and-delete-dialog branch May 10, 2026 14:52
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