vision-agents-plugins-pocket 0.2.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vision_agents_plugins_pocket-0.2.10/.gitignore +90 -0
- vision_agents_plugins_pocket-0.2.10/PKG-INFO +68 -0
- vision_agents_plugins_pocket-0.2.10/README.md +54 -0
- vision_agents_plugins_pocket-0.2.10/pyproject.toml +40 -0
- vision_agents_plugins_pocket-0.2.10/vision_agents/plugins/pocket/__init__.py +3 -0
- vision_agents_plugins_pocket-0.2.10/vision_agents/plugins/pocket/tts.py +129 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.cursor/*
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
.Python
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
eggs/
|
|
14
|
+
.eggs/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
share/python-wheels/
|
|
21
|
+
pip-wheel-metadata/
|
|
22
|
+
MANIFEST
|
|
23
|
+
*.egg-info/
|
|
24
|
+
*.egg
|
|
25
|
+
|
|
26
|
+
# Installer logs
|
|
27
|
+
pip-log.txt
|
|
28
|
+
pip-delete-this-directory.txt
|
|
29
|
+
|
|
30
|
+
# Unit test / coverage reports
|
|
31
|
+
htmlcov/
|
|
32
|
+
.tox/
|
|
33
|
+
.nox/
|
|
34
|
+
.coverage
|
|
35
|
+
.coverage.*
|
|
36
|
+
.cache
|
|
37
|
+
coverage.xml
|
|
38
|
+
nosetests.xml
|
|
39
|
+
*.cover
|
|
40
|
+
*.py,cover
|
|
41
|
+
.hypothesis/
|
|
42
|
+
.pytest_cache/
|
|
43
|
+
|
|
44
|
+
# Type checker / lint caches
|
|
45
|
+
.mypy_cache/
|
|
46
|
+
.dmypy.json
|
|
47
|
+
dmypy.json
|
|
48
|
+
.pytype/
|
|
49
|
+
.pyre/
|
|
50
|
+
.ruff_cache/
|
|
51
|
+
|
|
52
|
+
# Environments
|
|
53
|
+
.venv
|
|
54
|
+
env/
|
|
55
|
+
venv/
|
|
56
|
+
ENV/
|
|
57
|
+
env.bak/
|
|
58
|
+
venv.bak/
|
|
59
|
+
.env
|
|
60
|
+
.env.local
|
|
61
|
+
.env.*.local
|
|
62
|
+
.env.bak
|
|
63
|
+
pyvenv.cfg
|
|
64
|
+
.python-version
|
|
65
|
+
|
|
66
|
+
# Editors / IDEs
|
|
67
|
+
.vscode/
|
|
68
|
+
.idea/
|
|
69
|
+
|
|
70
|
+
# Jupyter Notebook
|
|
71
|
+
.ipynb_checkpoints/
|
|
72
|
+
|
|
73
|
+
# OS / Misc
|
|
74
|
+
.DS_Store
|
|
75
|
+
*.log
|
|
76
|
+
|
|
77
|
+
# Tooling & repo-specific
|
|
78
|
+
pyrightconfig.json
|
|
79
|
+
shell.nix
|
|
80
|
+
bin/*
|
|
81
|
+
lib/*
|
|
82
|
+
stream-py/
|
|
83
|
+
|
|
84
|
+
# Artifacts / assets
|
|
85
|
+
*.pt
|
|
86
|
+
*.kef
|
|
87
|
+
*.onnx
|
|
88
|
+
profile.html
|
|
89
|
+
|
|
90
|
+
/opencode.json
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vision-agents-plugins-pocket
|
|
3
|
+
Version: 0.2.10
|
|
4
|
+
Summary: Pocket TTS integration for Vision Agents - lightweight CPU-based text-to-speech
|
|
5
|
+
Project-URL: Documentation, https://visionagents.ai/
|
|
6
|
+
Project-URL: Website, https://visionagents.ai/
|
|
7
|
+
Project-URL: Source, https://github.com/GetStream/Vision-Agents
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: AI,TTS,agents,kyutai,pocket-tts,text-to-speech,voice agents
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: pocket-tts>=0.1.0
|
|
12
|
+
Requires-Dist: vision-agents
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Pocket TTS Plugin
|
|
16
|
+
|
|
17
|
+
A lightweight Text-to-Speech (TTS) plugin for [Vision Agents](https://github.com/GetStream/Vision-Agents) powered by Kyutai's [Pocket TTS](https://huggingface.co/kyutai/pocket-tts) model. Runs efficiently on CPU with low latency (~200ms) and supports voice cloning.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Runs on CPU - no GPU required
|
|
22
|
+
- Small model size (100M parameters)
|
|
23
|
+
- Low latency (~200ms to first audio)
|
|
24
|
+
- Voice cloning support
|
|
25
|
+
- Built-in voice selection
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv add vision-agents[pocket]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from vision_agents.plugins import pocket
|
|
37
|
+
|
|
38
|
+
# Create TTS with default voice
|
|
39
|
+
tts = pocket.TTS()
|
|
40
|
+
|
|
41
|
+
# Or specify a built-in voice
|
|
42
|
+
tts = pocket.TTS(voice="marius")
|
|
43
|
+
|
|
44
|
+
# Or use a custom voice for cloning
|
|
45
|
+
tts = pocket.TTS(voice="path/to/your/voice.wav")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
| Parameter | Description | Values |
|
|
51
|
+
|-----------|-------------|--------|
|
|
52
|
+
| `voice` | Built-in voice name or path to custom wav file | `"alba"` (default), `"marius"`, `"javert"`, `"jean"`, `"fantine"`, `"cosette"`, `"eponine"`, `"azelma"`, or custom path |
|
|
53
|
+
|
|
54
|
+
## Built-in Voices
|
|
55
|
+
|
|
56
|
+
- `alba` - Default voice
|
|
57
|
+
- `marius`
|
|
58
|
+
- `javert`
|
|
59
|
+
- `jean`
|
|
60
|
+
- `fantine`
|
|
61
|
+
- `cosette`
|
|
62
|
+
- `eponine`
|
|
63
|
+
- `azelma`
|
|
64
|
+
|
|
65
|
+
## Dependencies
|
|
66
|
+
|
|
67
|
+
- pocket-tts>=0.1.0
|
|
68
|
+
- PyTorch 2.5+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Pocket TTS Plugin
|
|
2
|
+
|
|
3
|
+
A lightweight Text-to-Speech (TTS) plugin for [Vision Agents](https://github.com/GetStream/Vision-Agents) powered by Kyutai's [Pocket TTS](https://huggingface.co/kyutai/pocket-tts) model. Runs efficiently on CPU with low latency (~200ms) and supports voice cloning.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Runs on CPU - no GPU required
|
|
8
|
+
- Small model size (100M parameters)
|
|
9
|
+
- Low latency (~200ms to first audio)
|
|
10
|
+
- Voice cloning support
|
|
11
|
+
- Built-in voice selection
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv add vision-agents[pocket]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from vision_agents.plugins import pocket
|
|
23
|
+
|
|
24
|
+
# Create TTS with default voice
|
|
25
|
+
tts = pocket.TTS()
|
|
26
|
+
|
|
27
|
+
# Or specify a built-in voice
|
|
28
|
+
tts = pocket.TTS(voice="marius")
|
|
29
|
+
|
|
30
|
+
# Or use a custom voice for cloning
|
|
31
|
+
tts = pocket.TTS(voice="path/to/your/voice.wav")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
| Parameter | Description | Values |
|
|
37
|
+
|-----------|-------------|--------|
|
|
38
|
+
| `voice` | Built-in voice name or path to custom wav file | `"alba"` (default), `"marius"`, `"javert"`, `"jean"`, `"fantine"`, `"cosette"`, `"eponine"`, `"azelma"`, or custom path |
|
|
39
|
+
|
|
40
|
+
## Built-in Voices
|
|
41
|
+
|
|
42
|
+
- `alba` - Default voice
|
|
43
|
+
- `marius`
|
|
44
|
+
- `javert`
|
|
45
|
+
- `jean`
|
|
46
|
+
- `fantine`
|
|
47
|
+
- `cosette`
|
|
48
|
+
- `eponine`
|
|
49
|
+
- `azelma`
|
|
50
|
+
|
|
51
|
+
## Dependencies
|
|
52
|
+
|
|
53
|
+
- pocket-tts>=0.1.0
|
|
54
|
+
- PyTorch 2.5+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vision-agents-plugins-pocket"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Pocket TTS integration for Vision Agents - lightweight CPU-based text-to-speech"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = ["pocket-tts", "TTS", "text-to-speech", "AI", "voice agents", "agents", "kyutai"]
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"vision-agents",
|
|
15
|
+
"pocket-tts>=0.1.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Documentation = "https://visionagents.ai/"
|
|
20
|
+
Website = "https://visionagents.ai/"
|
|
21
|
+
Source = "https://github.com/GetStream/Vision-Agents"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.version]
|
|
24
|
+
source = "vcs"
|
|
25
|
+
raw-options = { root = "..", search_parent_directories = true, fallback_version = "0.0.0" }
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = [".", "vision_agents"]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.sdist]
|
|
31
|
+
include = ["/vision_agents"]
|
|
32
|
+
|
|
33
|
+
[tool.uv.sources]
|
|
34
|
+
vision-agents = { workspace = true }
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=8.4.1",
|
|
39
|
+
"pytest-asyncio>=1.0.0",
|
|
40
|
+
]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
4
|
+
from typing import Any, AsyncIterator, Iterator, Literal
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from getstream.video.rtc.track_util import AudioFormat, PcmData
|
|
9
|
+
from vision_agents.core import tts
|
|
10
|
+
from vision_agents.core.warmup import Warmable
|
|
11
|
+
|
|
12
|
+
from pocket_tts import TTSModel
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
Voice = Literal[
|
|
17
|
+
"alba", "marius", "javert", "jean", "fantine", "cosette", "eponine", "azelma"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
VOICE_PATHS = {
|
|
21
|
+
"alba": "hf://kyutai/tts-voices/alba-mackenna/casual.wav",
|
|
22
|
+
"marius": "hf://kyutai/tts-voices/marius/casual.wav",
|
|
23
|
+
"javert": "hf://kyutai/tts-voices/javert/casual.wav",
|
|
24
|
+
"jean": "hf://kyutai/tts-voices/jean/casual.wav",
|
|
25
|
+
"fantine": "hf://kyutai/tts-voices/fantine/casual.wav",
|
|
26
|
+
"cosette": "hf://kyutai/tts-voices/cosette/casual.wav",
|
|
27
|
+
"eponine": "hf://kyutai/tts-voices/eponine/casual.wav",
|
|
28
|
+
"azelma": "hf://kyutai/tts-voices/azelma/casual.wav",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TTS(tts.TTS, Warmable[tuple[TTSModel, Any]]):
|
|
33
|
+
"""
|
|
34
|
+
Pocket TTS Text-to-Speech implementation.
|
|
35
|
+
|
|
36
|
+
A lightweight CPU-based TTS model from Kyutai with ~200ms latency
|
|
37
|
+
and voice cloning support.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
voice: Voice | str = "alba",
|
|
43
|
+
client: TTSModel | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Initialize Pocket TTS.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
voice: Built-in voice name or path to custom wav file for voice cloning.
|
|
50
|
+
client: Optional pre-initialized TTSModel instance.
|
|
51
|
+
"""
|
|
52
|
+
super().__init__(provider_name="pocket")
|
|
53
|
+
|
|
54
|
+
self.voice = voice
|
|
55
|
+
self._model: TTSModel | None = client
|
|
56
|
+
self._voice_state = None
|
|
57
|
+
self._executor = ThreadPoolExecutor(max_workers=4)
|
|
58
|
+
|
|
59
|
+
async def on_warmup(self) -> tuple[TTSModel, Any]:
|
|
60
|
+
if self._model is not None and self._voice_state is not None:
|
|
61
|
+
return (self._model, self._voice_state)
|
|
62
|
+
|
|
63
|
+
loop = asyncio.get_running_loop()
|
|
64
|
+
|
|
65
|
+
if self._model is not None:
|
|
66
|
+
model = self._model
|
|
67
|
+
else:
|
|
68
|
+
logger.info("Loading Pocket TTS model...")
|
|
69
|
+
model = await loop.run_in_executor(self._executor, TTSModel.load_model)
|
|
70
|
+
logger.info("Pocket TTS model loaded")
|
|
71
|
+
|
|
72
|
+
voice_path = VOICE_PATHS.get(self.voice, self.voice)
|
|
73
|
+
logger.info(f"Loading voice state for: {self.voice}")
|
|
74
|
+
voice_state = await loop.run_in_executor(
|
|
75
|
+
self._executor,
|
|
76
|
+
lambda: model.get_state_for_audio_prompt(voice_path),
|
|
77
|
+
)
|
|
78
|
+
logger.info("Voice state loaded")
|
|
79
|
+
return (model, voice_state)
|
|
80
|
+
|
|
81
|
+
def on_warmed_up(self, resource: tuple[TTSModel, Any]) -> None:
|
|
82
|
+
self._model, self._voice_state = resource
|
|
83
|
+
|
|
84
|
+
async def _ensure_loaded(self) -> None:
|
|
85
|
+
"""Ensure model and voice state are loaded."""
|
|
86
|
+
if self._model is None or self._voice_state is None:
|
|
87
|
+
resource = await self.on_warmup()
|
|
88
|
+
self.on_warmed_up(resource)
|
|
89
|
+
|
|
90
|
+
async def stream_audio(
|
|
91
|
+
self, text: str, *_, **__
|
|
92
|
+
) -> PcmData | Iterator[PcmData] | AsyncIterator[PcmData]:
|
|
93
|
+
"""
|
|
94
|
+
Convert text to speech using Pocket TTS.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
text: The text to convert to speech.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
PcmData containing the synthesized audio.
|
|
101
|
+
"""
|
|
102
|
+
await self._ensure_loaded()
|
|
103
|
+
assert self._model is not None
|
|
104
|
+
assert self._voice_state is not None
|
|
105
|
+
|
|
106
|
+
model = self._model
|
|
107
|
+
voice_state = self._voice_state
|
|
108
|
+
|
|
109
|
+
def _generate():
|
|
110
|
+
audio_tensor = model.generate_audio(voice_state, text)
|
|
111
|
+
audio_np = audio_tensor.numpy()
|
|
112
|
+
pcm16 = (np.clip(audio_np, -1.0, 1.0) * 32767.0).astype(np.int16)
|
|
113
|
+
return pcm16, model.sample_rate
|
|
114
|
+
|
|
115
|
+
loop = asyncio.get_running_loop()
|
|
116
|
+
samples, sample_rate = await loop.run_in_executor(self._executor, _generate)
|
|
117
|
+
|
|
118
|
+
return PcmData.from_numpy(
|
|
119
|
+
samples, sample_rate=sample_rate, channels=1, format=AudioFormat.S16
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
async def stop_audio(self) -> None:
|
|
123
|
+
"""Stop audio playback (no-op for Pocket TTS)."""
|
|
124
|
+
logger.info("Pocket TTS stop requested (no-op)")
|
|
125
|
+
|
|
126
|
+
async def close(self) -> None:
|
|
127
|
+
"""Close the TTS and cleanup resources."""
|
|
128
|
+
await super().close()
|
|
129
|
+
self._executor.shutdown(wait=False)
|