mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(deps): declare youtube-transcript-api in pyproject.toml [youtube] extra
skills/media/youtube-content/scripts/fetch_transcript.py and
optional-skills/productivity/memento-flashcards/scripts/youtube_quiz.py
both import youtube-transcript-api at runtime, but the package was not
listed in pyproject.toml. A fresh `uv sync` therefore omits it, and
both skills fail on first invocation with:
ModuleNotFoundError: No module named 'youtube_transcript_api'
Add a new [youtube] optional-dependency group with
youtube-transcript-api>=1.2.0 (the v1.x API surface the scripts already
use) and include it in [all] so standard installs pick it up.
Regression tests: TestPyprojectDeclaresYoutubeExtra verifies the extra
is present in pyproject.toml and included in [all].
Closes #22243
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a671d8a27a
commit
98e94beb1b
2 changed files with 95 additions and 0 deletions
|
|
@ -126,6 +126,13 @@ google = [
|
|||
"google-auth-oauthlib>=1.0,<2",
|
||||
"google-auth-httplib2>=0.2,<1",
|
||||
]
|
||||
youtube = [
|
||||
# Required by skills/media/youtube-content and
|
||||
# optional-skills/productivity/memento-flashcards (youtube_quiz.py).
|
||||
# Without this declaration uv sync omits the package and both skills fail
|
||||
# at first invocation with ModuleNotFoundError (issue #22243).
|
||||
"youtube-transcript-api>=1.2.0",
|
||||
]
|
||||
# `hermes dashboard` (localhost SPA + API). Not in core to keep the default install lean.
|
||||
web = ["fastapi>=0.104.0,<1", "uvicorn[standard]>=0.24.0,<1"]
|
||||
rl = [
|
||||
|
|
@ -163,6 +170,7 @@ all = [
|
|||
"hermes-agent[mistral]",
|
||||
"hermes-agent[bedrock]",
|
||||
"hermes-agent[web]",
|
||||
"hermes-agent[youtube]",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
|
|
|||
87
tests/skills/test_fetch_transcript.py
Normal file
87
tests/skills/test_fetch_transcript.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"""Tests for skills/media/youtube-content/scripts/fetch_transcript.py (issue #22243)."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPTS_DIR = Path(__file__).resolve().parents[2] / "skills" / "media" / "youtube-content" / "scripts"
|
||||
sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
|
||||
import fetch_transcript
|
||||
|
||||
|
||||
class TestExtractVideoId:
|
||||
def test_standard_watch_url(self):
|
||||
assert fetch_transcript.extract_video_id("https://www.youtube.com/watch?v=dQw4w9WgXcQ") == "dQw4w9WgXcQ"
|
||||
|
||||
def test_short_url(self):
|
||||
assert fetch_transcript.extract_video_id("https://youtu.be/dQw4w9WgXcQ") == "dQw4w9WgXcQ"
|
||||
|
||||
def test_bare_video_id(self):
|
||||
assert fetch_transcript.extract_video_id("dQw4w9WgXcQ") == "dQw4w9WgXcQ"
|
||||
|
||||
def test_shorts_url(self):
|
||||
assert fetch_transcript.extract_video_id("https://www.youtube.com/shorts/dQw4w9WgXcQ") == "dQw4w9WgXcQ"
|
||||
|
||||
def test_embed_url(self):
|
||||
assert fetch_transcript.extract_video_id("https://www.youtube.com/embed/dQw4w9WgXcQ") == "dQw4w9WgXcQ"
|
||||
|
||||
def test_with_extra_params(self):
|
||||
assert fetch_transcript.extract_video_id("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=42") == "dQw4w9WgXcQ"
|
||||
|
||||
|
||||
class TestFormatTimestamp:
|
||||
def test_seconds_only(self):
|
||||
assert fetch_transcript.format_timestamp(90) == "1:30"
|
||||
|
||||
def test_with_hours(self):
|
||||
assert fetch_transcript.format_timestamp(3661) == "1:01:01"
|
||||
|
||||
def test_zero(self):
|
||||
assert fetch_transcript.format_timestamp(0) == "0:00"
|
||||
|
||||
def test_minutes_only(self):
|
||||
assert fetch_transcript.format_timestamp(600) == "10:00"
|
||||
|
||||
|
||||
class TestFetchTranscriptImportError:
|
||||
def test_missing_dep_exits_with_message(self, capsys):
|
||||
"""fetch_transcript exits with code 1 and prints install hint when package missing (issue #22243)."""
|
||||
import builtins
|
||||
real_import = builtins.__import__
|
||||
|
||||
def mock_import(name, *args, **kwargs):
|
||||
if name == "youtube_transcript_api":
|
||||
raise ImportError("No module named 'youtube_transcript_api'")
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
with mock.patch("builtins.__import__", side_effect=mock_import):
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
fetch_transcript.fetch_transcript("dQw4w9WgXcQ")
|
||||
assert exc_info.value.code == 1
|
||||
captured = capsys.readouterr()
|
||||
assert "youtube-transcript-api" in captured.err
|
||||
|
||||
|
||||
class TestPyprojectDeclaresYoutubeExtra:
|
||||
def test_youtube_extra_declared_in_pyproject(self):
|
||||
"""youtube-transcript-api must be listed in pyproject.toml [youtube] extra (issue #22243)."""
|
||||
import tomllib
|
||||
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||
with pyproject_path.open("rb") as f:
|
||||
data = tomllib.load(f)
|
||||
extras = data.get("project", {}).get("optional-dependencies", {})
|
||||
assert "youtube" in extras, "Missing [youtube] extra in pyproject.toml"
|
||||
youtube_deps = " ".join(extras["youtube"])
|
||||
assert "youtube-transcript-api" in youtube_deps
|
||||
|
||||
def test_youtube_extra_included_in_all(self):
|
||||
"""[all] extra must include hermes-agent[youtube] (issue #22243)."""
|
||||
import tomllib
|
||||
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||
with pyproject_path.open("rb") as f:
|
||||
data = tomllib.load(f)
|
||||
all_deps = " ".join(data["project"]["optional-dependencies"].get("all", []))
|
||||
assert "youtube" in all_deps, "[all] extra does not include hermes-agent[youtube]"
|
||||
Loading…
Add table
Add a link
Reference in a new issue