Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Tests

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
gradio-runtime:
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"

- name: Install Gradio runtime test dependencies
run: |
python -m pip install --disable-pip-version-check \
pytest==8.3.5 \
"gradio==4.44.1" \
"huggingface_hub<1.0" \
"starlette<1.0"
python -m pip check

- name: Test Gradio runtime compatibility
run: python -m pytest -q tests/test_gradio_runtime_compat.py
13 changes: 4 additions & 9 deletions funclip/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from llm.twelvelabs_api import call_twelvelabs_pegasus
from utils.trans_utils import extract_timestamps
from introduction import top_md_1, top_md_3, top_md_4
from launch_config import build_launch_kwargs


def create_asr_model(model_name, lang, auto_model_cls=AutoModel):
Expand Down Expand Up @@ -66,10 +67,6 @@ def create_asr_model(model_name, lang, auto_model_cls=AutoModel):
audio_clipper = VideoClipper(funasr_model)
audio_clipper.lang = args.lang

server_name='127.0.0.1'
if args.listen:
server_name = '0.0.0.0'

def save_text_to_file(content, extension, output_dir=None):
if not content:
return None
Expand Down Expand Up @@ -373,8 +370,6 @@ def AI_clip_subti(LLM_res, dest_text, video_spk_input, start_ost, end_ost, video
],
outputs=[video_output, audio_output, clip_message, srt_clipped])

# start gradio service in local or share
if args.listen:
funclip_service.launch(share=args.share, server_port=args.port, server_name=server_name, inbrowser=False)
else:
funclip_service.launch(share=args.share, server_port=args.port, server_name=server_name)
funclip_service.launch(
**build_launch_kwargs(share=args.share, port=args.port, listen=args.listen)
)
16 changes: 16 additions & 0 deletions funclip/launch_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Launch policy for the FunClip Gradio service."""


def build_launch_kwargs(*, share, port, listen):
kwargs = {
"share": share,
"server_port": port,
"server_name": "127.0.0.1",
}
if listen:
kwargs.update(
server_name="0.0.0.0",
inbrowser=False,
_frontend=False,
)
return kwargs
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ huggingface_hub>=0.19.3,<1.0
moviepy==1.0.3
numpy==1.26.4
gradio>=4.31.3,<5.0
starlette<1.0
modelscope
torch>=1.13
torchaudio
Expand Down
84 changes: 84 additions & 0 deletions tests/test_gradio_runtime_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Regression tests for the supported Gradio/Starlette launch contract."""

import ast
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
LAUNCH_PATH = ROOT / "funclip" / "launch.py"


def test_gradio4_excludes_breaking_starlette_releases():
requirements = {
line.strip()
for line in (ROOT / "requirements.txt").read_text(encoding="utf-8").splitlines()
if line.strip() and not line.lstrip().startswith("#")
}

assert "gradio>=4.31.3,<5.0" in requirements
assert "starlette<1.0" in requirements


def test_supported_gradio_stack_renders_index():
import gradio
from starlette.testclient import TestClient

with gradio.Blocks() as demo:
gradio.Markdown("FunClip runtime smoke test")

app = gradio.routes.App.create_app(demo)
response = TestClient(app).get("/")

assert response.status_code == 200
assert "gradio_config" in response.text


def test_local_launch_keeps_gradio_frontend_probe_enabled():
from funclip.launch_config import build_launch_kwargs

assert build_launch_kwargs(share=False, port=7860, listen=False) == {
"share": False,
"server_port": 7860,
"server_name": "127.0.0.1",
}


def test_explicit_listen_skips_only_the_local_frontend_probe():
from funclip.launch_config import build_launch_kwargs

assert build_launch_kwargs(share=False, port=12235, listen=True) == {
"share": False,
"server_port": 12235,
"server_name": "0.0.0.0",
"inbrowser": False,
"_frontend": False,
}


def test_explicit_share_choice_is_preserved():
from funclip.launch_config import build_launch_kwargs

kwargs = build_launch_kwargs(share=True, port=7860, listen=True)

assert kwargs["share"] is True


def test_launcher_does_not_patch_dependencies_or_retry_with_public_share():
tree = ast.parse(LAUNCH_PATH.read_text(encoding="utf-8"))
launch_calls = [
node
for node in ast.walk(tree)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "launch"
]
patched_attributes = [
target.attr
for node in ast.walk(tree)
if isinstance(node, (ast.Assign, ast.AnnAssign))
for target in (node.targets if isinstance(node, ast.Assign) else [node.target])
if isinstance(target, ast.Attribute)
and target.attr in {"TemplateResponse", "cache", "cache_size"}
]

assert len(launch_calls) == 1
assert patched_attributes == []