|
12 | 12 |
|
13 | 13 | import functools |
14 | 14 | import glob |
| 15 | +import hashlib |
15 | 16 | import os |
16 | 17 | import pathlib |
| 18 | +import platform |
17 | 19 | import re |
| 20 | +import shutil |
18 | 21 | import sys |
| 22 | +import tarfile |
19 | 23 | import tempfile |
| 24 | +import urllib.request |
20 | 25 |
|
21 | 26 | from packaging.version import Version |
22 | 27 |
|
|
46 | 51 | _PROJECT_DIR = str(pathlib.Path(__file__).parent) |
47 | 52 |
|
48 | 53 |
|
| 54 | +def _ensure_livekit_server(session: nox.Session) -> str: |
| 55 | + """Ensure a standalone livekit-server binary is available for LiveKit e2e tests.""" |
| 56 | + existing = shutil.which("livekit-server") |
| 57 | + if existing: |
| 58 | + return os.path.dirname(existing) |
| 59 | + |
| 60 | + system = platform.system().lower() |
| 61 | + machine = platform.machine().lower() |
| 62 | + arch = {"x86_64": "amd64", "amd64": "amd64", "aarch64": "arm64", "arm64": "arm64"}.get(machine) |
| 63 | + if arch is None: |
| 64 | + session.skip(f"No pinned livekit-server binary for architecture {machine!r}") |
| 65 | + |
| 66 | + if system != "linux": |
| 67 | + session.skip( |
| 68 | + "No pinned standalone livekit-server release asset is available for this platform; " |
| 69 | + "install livekit-server on PATH to run LiveKit e2e tests locally" |
| 70 | + ) |
| 71 | + |
| 72 | + cache_root = pathlib.Path(os.environ.get("BRAINTRUST_LIVEKIT_SERVER_DIR", ".nox/livekit-server")) |
| 73 | + install_dir = cache_root / LIVEKIT_SERVER_VERSION / f"{system}_{arch}" |
| 74 | + binary = install_dir / "livekit-server" |
| 75 | + if binary.exists(): |
| 76 | + return str(install_dir.resolve()) |
| 77 | + |
| 78 | + install_dir.mkdir(parents=True, exist_ok=True) |
| 79 | + asset = f"livekit_{LIVEKIT_SERVER_VERSION}_{system}_{arch}.tar.gz" |
| 80 | + url = f"https://github.com/livekit/livekit/releases/download/v{LIVEKIT_SERVER_VERSION}/{asset}" |
| 81 | + archive = install_dir / asset |
| 82 | + expected_sha256 = LIVEKIT_SERVER_SHA256[f"{system}_{arch}"] |
| 83 | + session.log(f"Downloading {url}") |
| 84 | + urllib.request.urlretrieve(url, archive) # noqa: S310 - pinned public release asset for test infra. |
| 85 | + actual_sha256 = hashlib.sha256(archive.read_bytes()).hexdigest() |
| 86 | + if actual_sha256 != expected_sha256: |
| 87 | + archive.unlink(missing_ok=True) |
| 88 | + session.error( |
| 89 | + f"SHA256 mismatch for {asset}: expected {expected_sha256}, got {actual_sha256}. " |
| 90 | + "Refusing to extract downloaded livekit-server archive." |
| 91 | + ) |
| 92 | + with tarfile.open(archive, "r:gz") as tar: |
| 93 | + if sys.version_info >= (3, 12): |
| 94 | + tar.extract("livekit-server", path=install_dir, filter="data") |
| 95 | + else: |
| 96 | + tar.extract("livekit-server", path=install_dir) # noqa: S202 |
| 97 | + binary.chmod(0o755) |
| 98 | + archive.unlink() |
| 99 | + return str(install_dir.resolve()) |
| 100 | + |
| 101 | + |
49 | 102 | def _install_group_locked(session: nox.Session, *group_names: str) -> None: |
50 | 103 | """Install deps from one or more dependency groups using the lockfile. |
51 | 104 |
|
@@ -128,6 +181,11 @@ def _pinned_python_version(): |
128 | 181 |
|
129 | 182 | SILENT_INSTALLS = True |
130 | 183 | LATEST = "latest" |
| 184 | +LIVEKIT_SERVER_VERSION = "1.11.0" |
| 185 | +LIVEKIT_SERVER_SHA256 = { |
| 186 | + "linux_amd64": "3e76ed51ecdfefc3005e4257095dccd1ccc8f8b77517d9f2353de7906650b68b", |
| 187 | + "linux_arm64": "6741466bc12e75544338292ab2c1c02c02f3c626568230b5548fffc53e5a87ff", |
| 188 | +} |
131 | 189 | ERROR_CODES = tuple(range(1, 256)) |
132 | 190 | INTERNAL_TEST_FLAGS = {"--wheel", "--disable-vcr"} |
133 | 191 | GENERATED_LINT_EXCLUDES = { |
@@ -266,6 +324,27 @@ def test_agno(session, version): |
266 | 324 | _run_tests(session, f"{INTEGRATION_DIR}/agno/test_workflow.py", version=version) |
267 | 325 |
|
268 | 326 |
|
| 327 | +LIVEKIT_AGENTS_VERSIONS = _get_matrix_versions("livekit-agents") |
| 328 | + |
| 329 | + |
| 330 | +@nox.session() |
| 331 | +@nox.parametrize("version", LIVEKIT_AGENTS_VERSIONS, ids=LIVEKIT_AGENTS_VERSIONS) |
| 332 | +def test_livekit_agents(session, version): |
| 333 | + if sys.version_info >= (3, 14): |
| 334 | + session.skip("LiveKit Agents Silero VAD depends on onnxruntime, which does not ship Python 3.14 wheels") |
| 335 | + _install_test_deps(session) |
| 336 | + _install_matrix_dep(session, "livekit-agents", version) |
| 337 | + _install_group_locked(session, "test-livekit-agents") |
| 338 | + livekit_server_dir = _ensure_livekit_server(session) |
| 339 | + env = { |
| 340 | + "LIVEKIT_URL": os.environ.get("LIVEKIT_URL", "ws://localhost:7880"), |
| 341 | + "LIVEKIT_API_KEY": os.environ.get("LIVEKIT_API_KEY", "devkey"), |
| 342 | + "LIVEKIT_API_SECRET": os.environ.get("LIVEKIT_API_SECRET", "secret"), |
| 343 | + "PATH": f"{livekit_server_dir}{os.pathsep}{os.environ.get('PATH', '')}", |
| 344 | + } |
| 345 | + _run_tests(session, f"{INTEGRATION_DIR}/livekit_agents/test_livekit_agents.py", version=version, env=env) |
| 346 | + |
| 347 | + |
269 | 348 | STRANDS_VERSIONS = _get_matrix_versions("strands-agents") |
270 | 349 |
|
271 | 350 |
|
|
0 commit comments