Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
567651e
Rename project to Agent Voice
yoav0gal Jul 26, 2026
91ddbf6
Keep read-aloud skill concise
yoav0gal Jul 26, 2026
ffcaf21
Embed README video and rename skill
yoav0gal Jul 26, 2026
807920f
Improve README sample video
yoav0gal Jul 26, 2026
b5a771a
Simplify Agent Voice 0.5.0 delivery
yoav0gal Jul 27, 2026
06b0652
Declare NumPy and ignore macOS metadata
yoav0gal Jul 27, 2026
a98b94d
Harden local service request contract
yoav0gal Jul 27, 2026
98352ca
Tighten service checks and test contracts
yoav0gal Jul 27, 2026
fce168d
Restore secure HTML recording players
yoav0gal Jul 27, 2026
ffe8319
update git ignore
yoav0gal Jul 27, 2026
49a29ad
Separate portable recording fallback
yoav0gal Jul 27, 2026
d9373ca
Add branded localhost recording viewer
yoav0gal Jul 27, 2026
0bb2fec
Use clean HTML player URLs
yoav0gal Jul 27, 2026
0aa7397
Simplify recording delivery guidance
yoav0gal Jul 27, 2026
976cfe4
Harden viewer startup on slow runners
yoav0gal Jul 27, 2026
c0930f8
Report viewer startup phase
yoav0gal Jul 27, 2026
ea737b7
Avoid DNS during viewer binding
yoav0gal Jul 27, 2026
442bc4a
Simplify speech delivery and harden local services
yoav0gal Jul 27, 2026
e59a119
Remove stale speak JSON flags
yoav0gal Jul 27, 2026
3fae26b
Refactor speaking orchestration
yoav0gal Jul 27, 2026
e388659
Move delivery prose to skill-managed Markdown templates
yoav0gal Jul 28, 2026
ccd433e
Create recording-delivery.md
yoav0gal Jul 28, 2026
3cc9912
Create recording-delivery.md
yoav0gal Jul 28, 2026
b3f8267
rec space
yoav0gal Jul 28, 2026
8e2b929
Verify package installs across platforms
yoav0gal Jul 28, 2026
dacb9fb
Update CI cache runtime
yoav0gal Jul 28, 2026
30670d1
Finalize agent speech skills for release
yoav0gal Jul 28, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

import json
import os
import platform
import re
import subprocess
import sys
import time
import urllib.request
import wave
from pathlib import Path

import imageio_ffmpeg

SERVICE_URL = "http://127.0.0.1:18765"


Expand All @@ -22,51 +26,57 @@ def run_cli(cli: Path, *args: str) -> dict[str, object]:
return json.loads(completed.stdout.strip().splitlines()[-1])


def validate_wav(path: Path) -> None:
with wave.open(str(path), "rb") as audio:
assert audio.getnchannels() == 1
assert audio.getframerate() == 24_000
assert audio.getnframes() > 0
def validate_decodable(path: Path) -> None:
ffmpeg = imageio_ffmpeg.get_ffmpeg_exe()
assert "imageio_ffmpeg" in Path(ffmpeg).as_posix()
completed = subprocess.run(
[
"ffprobe",
ffmpeg,
"-v",
"error",
"-select_streams",
"a:0",
"-show_entries",
"stream=codec_name,sample_rate,channels",
"-of",
"json",
"-i",
str(path),
"-f",
"s16le",
"-acodec",
"pcm_s16le",
"pipe:1",
],
check=True,
capture_output=True,
text=True,
)
stream = json.loads(completed.stdout)["streams"][0]
assert stream == {
"codec_name": "pcm_s16le",
"sample_rate": "24000",
"channels": 1,
}
assert completed.stdout


def validate_wav(path: Path) -> None:
with wave.open(str(path), "rb") as audio:
assert audio.getnchannels() == 1
assert audio.getframerate() == 24_000
assert audio.getnframes() > 0
validate_decodable(path)


def validate_mp3(path: Path) -> None:
assert path.suffix == ".mp3"
validate_decodable(path)


def check_doctor(report: dict[str, object], service_status: str) -> None:
assert report["ok"] is True
checks = {check["name"]: check for check in report["checks"]}
assert checks["model"]["status"] == "pass"
assert checks["runtime"]["status"] == "pass"
assert checks["playback"]["status"] == "warn"
assert "experimental" in checks["playback"]["detail"]
assert checks["compressed audio"]["status"] == "pass"
assert "bundled by imageio-ffmpeg" in checks["compressed audio"]["detail"]
assert checks["playback"]["status"] in {"pass", "warn"}
assert "miniaudio" in checks["playback"]["detail"]
assert checks["service"]["status"] == service_status


def main() -> None:
if sys.platform != "win32":
raise RuntimeError("This verification is intentionally Windows-only")
cli = Path(sys.argv[1]).resolve()
output_dir = Path(os.environ["RUNNER_TEMP"]) / "kokoro-windows-e2e"
system = platform.system()
output_dir = Path(os.environ["RUNNER_TEMP"]) / "agent-voice-package-e2e"
output_dir.mkdir(parents=True, exist_ok=True)

subprocess.run([str(cli), "setup", "--model", "int8"], check=True)
Expand All @@ -77,17 +87,42 @@ def main() -> None:
local = run_cli(
cli,
"speak",
"Windows generation verification.",
f"{system} generation verification.",
"--service",
"off",
"--output",
str(local_wav),
"--json",
)
assert local["backend"] == "local"
assert local["played"] is False
validate_wav(local_wav)

labeled = run_cli(
cli,
"speak",
f"{system} labeled speed verification.",
"--service",
"off",
"--label",
"Package E2E",
"--format",
"mp3",
"--speed",
"1.5",
)
labeled_path = Path(str(labeled["path"]))
assert labeled["backend"] == "local"
assert labeled["speed"] == 1.5
assert re.fullmatch(
r"Package-E2E-\d{2}-\d{2}-\d{2}-at-\d{2}-\d{2}\.mp3",
labeled_path.name,
)
assert (
labeled_path.parent
== (Path(os.environ["AGENT_VOICE_HOME"]) / "recordings").resolve()
)
validate_mp3(labeled_path)

log_path = output_dir / "service.log"
with log_path.open("w", encoding="utf-8") as log:
service = subprocess.Popen(
Expand Down Expand Up @@ -120,14 +155,13 @@ def main() -> None:
remote = run_cli(
cli,
"speak",
"Windows localhost service verification.",
f"{system} localhost service verification.",
"--service",
"required",
"on",
"--service-url",
SERVICE_URL,
"--output",
str(service_wav),
"--json",
)
assert remote["backend"] == "service"
assert remote["played"] is False
Expand All @@ -140,6 +174,7 @@ def main() -> None:
service.kill()
service.wait(timeout=10)
print(log_path.read_text(encoding="utf-8", errors="replace"))
print(f"Verified installed package on {system}")


if __name__ == "__main__":
Expand Down
62 changes: 38 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
Expand All @@ -26,28 +31,39 @@ jobs:
enable-cache: true
python-version: ${{ matrix.python-version }}

- name: Install FFmpeg on Linux
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes ffmpeg

- name: Install FFmpeg on macOS
if: runner.os == 'macOS'
run: brew install ffmpeg
- name: Cache verified Kokoro model
if: matrix.python-version == '3.13'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ runner.temp }}/agent-voice-data/models
key: kokoro-v1-int8-${{ runner.os }}-${{ runner.arch }}

- name: Lint
run: uv run --frozen ruff check src tests
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
run: uv run --frozen ruff check src tests .github/scripts

- name: Test
run: uv run --frozen pytest -q

- name: Build packages
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
- name: Build package
if: matrix.python-version == '3.13'
run: uv build

- name: Install built wheel
if: matrix.python-version == '3.13'
run: |
uv venv --python ${{ matrix.python-version }} .ci-venv
uv pip install --python .ci-venv/bin/python dist/*.whl

- name: Verify installed package with real model, audio, doctor, and service
if: matrix.python-version == '3.13'
env:
AGENT_VOICE_HOME: ${{ runner.temp }}/agent-voice-data
run: |
.ci-venv/bin/python .github/scripts/verify_package.py .ci-venv/bin/agent-voice

windows:
name: windows-latest / Python ${{ matrix.python-version }} / package E2E
name: windows-latest / Python ${{ matrix.python-version }}
runs-on: windows-latest
strategy:
fail-fast: false
Expand All @@ -63,34 +79,32 @@ jobs:
enable-cache: true
python-version: ${{ matrix.python-version }}

- name: Install FFmpeg
run: choco install ffmpeg --version=7.1.1 --yes --no-progress --allow-downgrade

- name: Cache verified Kokoro model
uses: actions/cache@v4
if: matrix.python-version == '3.13'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ runner.temp }}/kokoro-data/models
key: kokoro-v1-int8-windows

- name: Lint
run: uv run --frozen ruff check src tests .github/scripts
path: ${{ runner.temp }}/agent-voice-data/models
key: kokoro-v1-int8-${{ runner.os }}-${{ runner.arch }}

- name: Test
run: uv run --frozen pytest -q

- name: Build package
if: matrix.python-version == '3.13'
run: uv build

- name: Install built wheel
if: matrix.python-version == '3.13'
shell: pwsh
run: |
uv venv --python ${{ matrix.python-version }} .ci-venv
$wheel = (Get-ChildItem dist\*.whl).FullName
uv pip install --python .ci-venv\Scripts\python.exe $wheel

- name: Verify installed package with real model, audio, doctor, and service
if: matrix.python-version == '3.13'
shell: pwsh
env:
KOKORO_HOME: ${{ runner.temp }}/kokoro-data
AGENT_VOICE_HOME: ${{ runner.temp }}/agent-voice-data
run: |
.ci-venv\Scripts\python.exe .github\scripts\verify_windows.py .ci-venv\Scripts\kokoro.exe
.ci-venv\Scripts\python.exe .github\scripts\verify_package.py .ci-venv\Scripts\agent-voice.exe
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/kokoro-cli
url: https://pypi.org/p/agent-voice
permissions:
id-token: write

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
.venv/
.DS_Store
__pycache__/
.pytest_cache/
.ruff_cache/
.agents/
skills-lock.json
*.pyc
*.egg-info/
dist/
config.json
service-start.lock
viewer.lock
viewer.json
models/*.onnx
models/*.bin
models/*.lock
recordings/*
IDEAS.md
!models/.gitkeep
!recordings/.gitkeep
21 changes: 0 additions & 21 deletions AGENTS.md

This file was deleted.

Loading