caudate-cli 0.1.0__py3-none-any.whl
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.
- api/__init__.py +5 -0
- api/anthropic_compat.py +1518 -0
- api/artifact_viewer.py +366 -0
- api/caudate_middleware.py +618 -0
- api/forge_bootstrapper_routes.py +377 -0
- api/forge_routes.py +630 -0
- api/forge_system_routes.py +294 -0
- api/openai_compat.py +1993 -0
- api/server.py +667 -0
- api/storyboard_page.py +677 -0
- caudate_cli-0.1.0.dist-info/METADATA +354 -0
- caudate_cli-0.1.0.dist-info/RECORD +153 -0
- caudate_cli-0.1.0.dist-info/WHEEL +5 -0
- caudate_cli-0.1.0.dist-info/entry_points.txt +2 -0
- caudate_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- caudate_cli-0.1.0.dist-info/top_level.txt +14 -0
- cognos_mcp/__init__.py +4 -0
- cognos_mcp/bridge.py +41 -0
- cognos_mcp/client.py +70 -0
- cognos_mcp/config.py +49 -0
- cognos_mcp/server.py +66 -0
- config.py +82 -0
- core/__init__.py +0 -0
- core/agent.py +468 -0
- core/agentic_loop.py +731 -0
- core/anthropic_auth.py +91 -0
- core/background.py +113 -0
- core/banner.py +134 -0
- core/bootstrap.py +292 -0
- core/citations.py +131 -0
- core/compaction.py +109 -0
- core/constitution.py +198 -0
- core/diff_viewer.py +87 -0
- core/export.py +85 -0
- core/file_refs.py +119 -0
- core/files.py +199 -0
- core/hooks.py +209 -0
- core/image.py +599 -0
- core/input.py +91 -0
- core/loop.py +238 -0
- core/memory_md.py +147 -0
- core/notifications.py +99 -0
- core/ownership.py +181 -0
- core/paste.py +81 -0
- core/permissions.py +210 -0
- core/plan_mode.py +215 -0
- core/sandbox_prompt.py +185 -0
- core/scheduler.py +195 -0
- core/schemas.py +202 -0
- core/session.py +90 -0
- core/settings.py +132 -0
- core/skills.py +398 -0
- core/slash_commands.py +977 -0
- core/statusline.py +61 -0
- core/subagent.py +300 -0
- core/thinking.py +50 -0
- core/updater.py +122 -0
- core/usage.py +109 -0
- core/worktree.py +93 -0
- execution/__init__.py +0 -0
- execution/executor.py +329 -0
- execution/plugins.py +108 -0
- execution/tools/__init__.py +0 -0
- execution/tools/agent_tool.py +107 -0
- execution/tools/agentic_tool.py +297 -0
- execution/tools/artifact_tool.py +191 -0
- execution/tools/ask_user_question_tool.py +137 -0
- execution/tools/base.py +81 -0
- execution/tools/calculator_tool.py +137 -0
- execution/tools/cognos_card_tool.py +124 -0
- execution/tools/cron_tool.py +215 -0
- execution/tools/datetime_tool.py +215 -0
- execution/tools/describe_image_tool.py +161 -0
- execution/tools/draw_tool.py +164 -0
- execution/tools/edit_image_tool.py +262 -0
- execution/tools/edit_tool.py +245 -0
- execution/tools/file_tool.py +90 -0
- execution/tools/find_anywhere_tool.py +255 -0
- execution/tools/forge_feature_tools.py +377 -0
- execution/tools/glob_tool.py +59 -0
- execution/tools/grep_tool.py +89 -0
- execution/tools/http_request_tool.py +224 -0
- execution/tools/load_skill_tool.py +104 -0
- execution/tools/longcat_avatar_tool.py +384 -0
- execution/tools/mcp_tool.py +100 -0
- execution/tools/notebook_tool.py +279 -0
- execution/tools/openapi_tool.py +440 -0
- execution/tools/plan_mode_tool.py +95 -0
- execution/tools/push_notification_tool.py +157 -0
- execution/tools/python_tool.py +61 -0
- execution/tools/respond_tool.py +40 -0
- execution/tools/sandbox_tool.py +378 -0
- execution/tools/search_tool.py +153 -0
- execution/tools/semantic_search_tool.py +106 -0
- execution/tools/shell_tool.py +283 -0
- execution/tools/speak_tool.py +134 -0
- execution/tools/storyboard_tool.py +727 -0
- execution/tools/system_info_tool.py +212 -0
- execution/tools/task_tool.py +323 -0
- execution/tools/think_tool.py +49 -0
- execution/tools/transcribe_audio_tool.py +86 -0
- execution/tools/update_memory_tool.py +92 -0
- execution/tools/web_fetch_tool.py +82 -0
- execution/tools/worktree_tool.py +174 -0
- llm/__init__.py +0 -0
- llm/fallback.py +116 -0
- llm/models.py +320 -0
- llm/provider.py +1356 -0
- llm/router.py +373 -0
- main.py +1889 -0
- memory/__init__.py +0 -0
- memory/episodic.py +99 -0
- memory/procedural.py +145 -0
- memory/semantic.py +71 -0
- memory/working.py +64 -0
- nn/__init__.py +43 -0
- nn/auto_evolve.py +245 -0
- nn/caudate.py +136 -0
- nn/config.py +141 -0
- nn/consolidator.py +81 -0
- nn/data.py +1635 -0
- nn/encoder.py +258 -0
- nn/forge_advisor.py +303 -0
- nn/format.py +235 -0
- nn/heads.py +432 -0
- nn/observer.py +994 -0
- nn/policy.py +214 -0
- nn/runtime.py +343 -0
- nn/scorer.py +175 -0
- nn/trainer.py +515 -0
- nn/vision.py +352 -0
- personality/__init__.py +23 -0
- personality/engine.py +129 -0
- personality/identity.py +144 -0
- personality/inner_voice.py +100 -0
- personality/mood.py +205 -0
- planning/__init__.py +0 -0
- planning/dev_server.py +221 -0
- planning/forge_models.py +718 -0
- planning/orchestrator.py +1363 -0
- planning/planner.py +451 -0
- planning/task_graph.py +61 -0
- reflection/__init__.py +0 -0
- reflection/meta_learner.py +156 -0
- reflection/reflector.py +127 -0
- ui/__init__.py +5 -0
- ui/display.py +88 -0
- voice/__init__.py +0 -0
- voice/conversation.py +125 -0
- voice/listener.py +111 -0
- voice/speaker.py +59 -0
- voice/stt.py +126 -0
- voice/tts.py +214 -0
voice/tts.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Text-to-speech backends.
|
|
2
|
+
|
|
3
|
+
Pluggable, mirroring `voice/stt.py`. Three implementations ship:
|
|
4
|
+
|
|
5
|
+
- **KokoroTTS** (default) — Kokoro-82M, MIT-licensed, sounds genuinely
|
|
6
|
+
human. ~330MB model, fast on CPU and very fast on GPU. Many
|
|
7
|
+
expressive voices (af_heart, af_bella, am_adam, bm_george, ...).
|
|
8
|
+
Pip: `pip install kokoro soundfile`. Released by hexgrad on HF.
|
|
9
|
+
|
|
10
|
+
- **PiperTTS** (fallback) — what shipped before. Very lightweight,
|
|
11
|
+
runs anywhere, but obviously robotic. Kept for low-resource hosts.
|
|
12
|
+
|
|
13
|
+
- **XTTSTTS** (optional, voice cloning) — Coqui XTTS-v2. Heavy
|
|
14
|
+
(~2GB) but can clone any voice from a 6-second sample. Useful if
|
|
15
|
+
you want Cognos to sound like a specific person.
|
|
16
|
+
|
|
17
|
+
All backends expose `synthesize(text) -> (samples_int16, sample_rate)`.
|
|
18
|
+
The Speaker class plays them through `sounddevice`.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import io
|
|
24
|
+
import logging
|
|
25
|
+
import os
|
|
26
|
+
import wave
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, Protocol
|
|
29
|
+
|
|
30
|
+
# Set BEFORE importing kokoro / transformers. A broken TF install can
|
|
31
|
+
# poison transformers' lazy loader via the image_transforms module.
|
|
32
|
+
os.environ.setdefault("USE_TF", "0")
|
|
33
|
+
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# --- Protocol --------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TTS(Protocol):
|
|
43
|
+
"""Synthesize text to int16 PCM samples + sample rate."""
|
|
44
|
+
|
|
45
|
+
def synthesize(self, text: str) -> tuple[np.ndarray, int]:
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# --- Kokoro (default) ------------------------------------------------
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Friendly catalog. Real list lives in the kokoro voices/ folder; these
|
|
53
|
+
# are the most useful ones documented as of 2026-04.
|
|
54
|
+
KOKORO_VOICES = {
|
|
55
|
+
"af_heart": "American English · warm, expressive female",
|
|
56
|
+
"af_bella": "American English · upbeat female",
|
|
57
|
+
"af_sarah": "American English · clear female",
|
|
58
|
+
"af_nicole": "American English · soft female",
|
|
59
|
+
"am_adam": "American English · neutral male",
|
|
60
|
+
"am_michael": "American English · warm male",
|
|
61
|
+
"bf_emma": "British English · expressive female",
|
|
62
|
+
"bf_isabella": "British English · soft female",
|
|
63
|
+
"bm_george": "British English · neutral male",
|
|
64
|
+
"bm_lewis": "British English · warm male",
|
|
65
|
+
}
|
|
66
|
+
DEFAULT_KOKORO_VOICE = "af_heart"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class KokoroTTS:
|
|
70
|
+
"""Kokoro-82M TTS via the `kokoro` pip package.
|
|
71
|
+
|
|
72
|
+
Lazy-loads on first synthesize() call. Picks the lang_code from the
|
|
73
|
+
voice name's first letter (`a` for American, `b` for British, etc.)
|
|
74
|
+
so callers only pass one identifier.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self, voice: str = DEFAULT_KOKORO_VOICE):
|
|
78
|
+
self.voice = voice
|
|
79
|
+
self._pipeline = None
|
|
80
|
+
self._sample_rate = 24000
|
|
81
|
+
|
|
82
|
+
def _ensure_loaded(self) -> None:
|
|
83
|
+
if self._pipeline is not None:
|
|
84
|
+
return
|
|
85
|
+
try:
|
|
86
|
+
from kokoro import KPipeline
|
|
87
|
+
except ImportError as e:
|
|
88
|
+
raise RuntimeError(
|
|
89
|
+
"Kokoro TTS requested but `kokoro` is not installed. "
|
|
90
|
+
"Run: pip install kokoro soundfile"
|
|
91
|
+
) from e
|
|
92
|
+
# First letter of the voice name encodes the language family
|
|
93
|
+
# (kokoro convention: a=American, b=British, e=European Spanish,
|
|
94
|
+
# j=Japanese, etc).
|
|
95
|
+
lang_code = (self.voice[:1] or "a").lower()
|
|
96
|
+
logger.info(f"Loading Kokoro pipeline (voice={self.voice}, lang={lang_code})")
|
|
97
|
+
self._pipeline = KPipeline(lang_code=lang_code)
|
|
98
|
+
logger.info("Kokoro loaded.")
|
|
99
|
+
|
|
100
|
+
def synthesize(self, text: str) -> tuple[np.ndarray, int]:
|
|
101
|
+
self._ensure_loaded()
|
|
102
|
+
audio_chunks: list[np.ndarray] = []
|
|
103
|
+
# KPipeline yields (graphemes, phonemes, audio). We only want audio.
|
|
104
|
+
for _, _, audio in self._pipeline(text, voice=self.voice):
|
|
105
|
+
if audio is None:
|
|
106
|
+
continue
|
|
107
|
+
arr = audio.cpu().numpy() if hasattr(audio, "cpu") else np.asarray(audio)
|
|
108
|
+
audio_chunks.append(arr.astype(np.float32))
|
|
109
|
+
if not audio_chunks:
|
|
110
|
+
return np.zeros(0, dtype=np.int16), self._sample_rate
|
|
111
|
+
full = np.concatenate(audio_chunks)
|
|
112
|
+
# Kokoro emits float in [-1, 1]; convert to int16 PCM for sounddevice.
|
|
113
|
+
full = np.clip(full, -1.0, 1.0)
|
|
114
|
+
return (full * 32767.0).astype(np.int16), self._sample_rate
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# --- Piper (fallback, kept for compat) ------------------------------
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class PiperTTS:
|
|
121
|
+
"""Piper TTS — original backend. Fast, light, robotic."""
|
|
122
|
+
|
|
123
|
+
def __init__(self, voice_path: str | Path | None = None):
|
|
124
|
+
from pathlib import Path as _P
|
|
125
|
+
default = _P(__file__).parent.parent / "data" / "voices" / "en_US-amy-medium.onnx"
|
|
126
|
+
self.voice_path = str(voice_path or default)
|
|
127
|
+
self._voice = None
|
|
128
|
+
|
|
129
|
+
def _ensure_loaded(self) -> None:
|
|
130
|
+
if self._voice is not None:
|
|
131
|
+
return
|
|
132
|
+
try:
|
|
133
|
+
from piper import PiperVoice
|
|
134
|
+
except ImportError as e:
|
|
135
|
+
raise RuntimeError(
|
|
136
|
+
"Piper TTS requested but `piper-tts` is not installed."
|
|
137
|
+
) from e
|
|
138
|
+
logger.info(f"Loading Piper voice from {self.voice_path}")
|
|
139
|
+
self._voice = PiperVoice.load(self.voice_path)
|
|
140
|
+
logger.info("Piper voice loaded.")
|
|
141
|
+
|
|
142
|
+
def synthesize(self, text: str) -> tuple[np.ndarray, int]:
|
|
143
|
+
self._ensure_loaded()
|
|
144
|
+
chunks: list[bytes] = []
|
|
145
|
+
for chunk in self._voice.synthesize(text):
|
|
146
|
+
chunks.append(chunk.audio_int16_bytes)
|
|
147
|
+
if not chunks:
|
|
148
|
+
return np.zeros(0, dtype=np.int16), self._voice.config.sample_rate
|
|
149
|
+
raw = b"".join(chunks)
|
|
150
|
+
audio = np.frombuffer(raw, dtype=np.int16)
|
|
151
|
+
return audio, self._voice.config.sample_rate
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# --- XTTS (optional, voice cloning) ---------------------------------
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class XTTSTTS:
|
|
158
|
+
"""Coqui XTTS-v2 — natural and clones voices from a short sample.
|
|
159
|
+
|
|
160
|
+
Heavier than Kokoro (~2GB model, slower first-token latency). Useful
|
|
161
|
+
when you want Cognos to sound like a specific recorded voice.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
speaker_wav: str | Path | None = None,
|
|
167
|
+
language: str = "en",
|
|
168
|
+
model_name: str = "tts_models/multilingual/multi-dataset/xtts_v2",
|
|
169
|
+
):
|
|
170
|
+
self.speaker_wav = str(speaker_wav) if speaker_wav else None
|
|
171
|
+
self.language = language
|
|
172
|
+
self.model_name = model_name
|
|
173
|
+
self._tts = None
|
|
174
|
+
self._sample_rate = 24000
|
|
175
|
+
|
|
176
|
+
def _ensure_loaded(self) -> None:
|
|
177
|
+
if self._tts is not None:
|
|
178
|
+
return
|
|
179
|
+
try:
|
|
180
|
+
from TTS.api import TTS as _TTS
|
|
181
|
+
except ImportError as e:
|
|
182
|
+
raise RuntimeError(
|
|
183
|
+
"XTTS requested but `TTS` is not installed. "
|
|
184
|
+
"Run: pip install TTS"
|
|
185
|
+
) from e
|
|
186
|
+
logger.info(f"Loading XTTS model {self.model_name}")
|
|
187
|
+
self._tts = _TTS(self.model_name, progress_bar=False)
|
|
188
|
+
logger.info("XTTS loaded.")
|
|
189
|
+
|
|
190
|
+
def synthesize(self, text: str) -> tuple[np.ndarray, int]:
|
|
191
|
+
self._ensure_loaded()
|
|
192
|
+
kwargs: dict[str, Any] = {"text": text, "language": self.language}
|
|
193
|
+
if self.speaker_wav:
|
|
194
|
+
kwargs["speaker_wav"] = self.speaker_wav
|
|
195
|
+
wav = self._tts.tts(**kwargs)
|
|
196
|
+
arr = np.asarray(wav, dtype=np.float32)
|
|
197
|
+
arr = np.clip(arr, -1.0, 1.0)
|
|
198
|
+
return (arr * 32767.0).astype(np.int16), self._sample_rate
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# --- Factory --------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def make_tts(name: str = "kokoro", **kwargs: Any) -> TTS:
|
|
205
|
+
n = name.lower()
|
|
206
|
+
if n == "kokoro":
|
|
207
|
+
return KokoroTTS(**kwargs)
|
|
208
|
+
if n == "piper":
|
|
209
|
+
return PiperTTS(**kwargs)
|
|
210
|
+
if n in ("xtts", "coqui"):
|
|
211
|
+
return XTTSTTS(**kwargs)
|
|
212
|
+
raise ValueError(
|
|
213
|
+
f"Unknown TTS backend: {name!r} (try 'kokoro', 'piper', or 'xtts')"
|
|
214
|
+
)
|