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.
Files changed (153) hide show
  1. api/__init__.py +5 -0
  2. api/anthropic_compat.py +1518 -0
  3. api/artifact_viewer.py +366 -0
  4. api/caudate_middleware.py +618 -0
  5. api/forge_bootstrapper_routes.py +377 -0
  6. api/forge_routes.py +630 -0
  7. api/forge_system_routes.py +294 -0
  8. api/openai_compat.py +1993 -0
  9. api/server.py +667 -0
  10. api/storyboard_page.py +677 -0
  11. caudate_cli-0.1.0.dist-info/METADATA +354 -0
  12. caudate_cli-0.1.0.dist-info/RECORD +153 -0
  13. caudate_cli-0.1.0.dist-info/WHEEL +5 -0
  14. caudate_cli-0.1.0.dist-info/entry_points.txt +2 -0
  15. caudate_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  16. caudate_cli-0.1.0.dist-info/top_level.txt +14 -0
  17. cognos_mcp/__init__.py +4 -0
  18. cognos_mcp/bridge.py +41 -0
  19. cognos_mcp/client.py +70 -0
  20. cognos_mcp/config.py +49 -0
  21. cognos_mcp/server.py +66 -0
  22. config.py +82 -0
  23. core/__init__.py +0 -0
  24. core/agent.py +468 -0
  25. core/agentic_loop.py +731 -0
  26. core/anthropic_auth.py +91 -0
  27. core/background.py +113 -0
  28. core/banner.py +134 -0
  29. core/bootstrap.py +292 -0
  30. core/citations.py +131 -0
  31. core/compaction.py +109 -0
  32. core/constitution.py +198 -0
  33. core/diff_viewer.py +87 -0
  34. core/export.py +85 -0
  35. core/file_refs.py +119 -0
  36. core/files.py +199 -0
  37. core/hooks.py +209 -0
  38. core/image.py +599 -0
  39. core/input.py +91 -0
  40. core/loop.py +238 -0
  41. core/memory_md.py +147 -0
  42. core/notifications.py +99 -0
  43. core/ownership.py +181 -0
  44. core/paste.py +81 -0
  45. core/permissions.py +210 -0
  46. core/plan_mode.py +215 -0
  47. core/sandbox_prompt.py +185 -0
  48. core/scheduler.py +195 -0
  49. core/schemas.py +202 -0
  50. core/session.py +90 -0
  51. core/settings.py +132 -0
  52. core/skills.py +398 -0
  53. core/slash_commands.py +977 -0
  54. core/statusline.py +61 -0
  55. core/subagent.py +300 -0
  56. core/thinking.py +50 -0
  57. core/updater.py +122 -0
  58. core/usage.py +109 -0
  59. core/worktree.py +93 -0
  60. execution/__init__.py +0 -0
  61. execution/executor.py +329 -0
  62. execution/plugins.py +108 -0
  63. execution/tools/__init__.py +0 -0
  64. execution/tools/agent_tool.py +107 -0
  65. execution/tools/agentic_tool.py +297 -0
  66. execution/tools/artifact_tool.py +191 -0
  67. execution/tools/ask_user_question_tool.py +137 -0
  68. execution/tools/base.py +81 -0
  69. execution/tools/calculator_tool.py +137 -0
  70. execution/tools/cognos_card_tool.py +124 -0
  71. execution/tools/cron_tool.py +215 -0
  72. execution/tools/datetime_tool.py +215 -0
  73. execution/tools/describe_image_tool.py +161 -0
  74. execution/tools/draw_tool.py +164 -0
  75. execution/tools/edit_image_tool.py +262 -0
  76. execution/tools/edit_tool.py +245 -0
  77. execution/tools/file_tool.py +90 -0
  78. execution/tools/find_anywhere_tool.py +255 -0
  79. execution/tools/forge_feature_tools.py +377 -0
  80. execution/tools/glob_tool.py +59 -0
  81. execution/tools/grep_tool.py +89 -0
  82. execution/tools/http_request_tool.py +224 -0
  83. execution/tools/load_skill_tool.py +104 -0
  84. execution/tools/longcat_avatar_tool.py +384 -0
  85. execution/tools/mcp_tool.py +100 -0
  86. execution/tools/notebook_tool.py +279 -0
  87. execution/tools/openapi_tool.py +440 -0
  88. execution/tools/plan_mode_tool.py +95 -0
  89. execution/tools/push_notification_tool.py +157 -0
  90. execution/tools/python_tool.py +61 -0
  91. execution/tools/respond_tool.py +40 -0
  92. execution/tools/sandbox_tool.py +378 -0
  93. execution/tools/search_tool.py +153 -0
  94. execution/tools/semantic_search_tool.py +106 -0
  95. execution/tools/shell_tool.py +283 -0
  96. execution/tools/speak_tool.py +134 -0
  97. execution/tools/storyboard_tool.py +727 -0
  98. execution/tools/system_info_tool.py +212 -0
  99. execution/tools/task_tool.py +323 -0
  100. execution/tools/think_tool.py +49 -0
  101. execution/tools/transcribe_audio_tool.py +86 -0
  102. execution/tools/update_memory_tool.py +92 -0
  103. execution/tools/web_fetch_tool.py +82 -0
  104. execution/tools/worktree_tool.py +174 -0
  105. llm/__init__.py +0 -0
  106. llm/fallback.py +116 -0
  107. llm/models.py +320 -0
  108. llm/provider.py +1356 -0
  109. llm/router.py +373 -0
  110. main.py +1889 -0
  111. memory/__init__.py +0 -0
  112. memory/episodic.py +99 -0
  113. memory/procedural.py +145 -0
  114. memory/semantic.py +71 -0
  115. memory/working.py +64 -0
  116. nn/__init__.py +43 -0
  117. nn/auto_evolve.py +245 -0
  118. nn/caudate.py +136 -0
  119. nn/config.py +141 -0
  120. nn/consolidator.py +81 -0
  121. nn/data.py +1635 -0
  122. nn/encoder.py +258 -0
  123. nn/forge_advisor.py +303 -0
  124. nn/format.py +235 -0
  125. nn/heads.py +432 -0
  126. nn/observer.py +994 -0
  127. nn/policy.py +214 -0
  128. nn/runtime.py +343 -0
  129. nn/scorer.py +175 -0
  130. nn/trainer.py +515 -0
  131. nn/vision.py +352 -0
  132. personality/__init__.py +23 -0
  133. personality/engine.py +129 -0
  134. personality/identity.py +144 -0
  135. personality/inner_voice.py +100 -0
  136. personality/mood.py +205 -0
  137. planning/__init__.py +0 -0
  138. planning/dev_server.py +221 -0
  139. planning/forge_models.py +718 -0
  140. planning/orchestrator.py +1363 -0
  141. planning/planner.py +451 -0
  142. planning/task_graph.py +61 -0
  143. reflection/__init__.py +0 -0
  144. reflection/meta_learner.py +156 -0
  145. reflection/reflector.py +127 -0
  146. ui/__init__.py +5 -0
  147. ui/display.py +88 -0
  148. voice/__init__.py +0 -0
  149. voice/conversation.py +125 -0
  150. voice/listener.py +111 -0
  151. voice/speaker.py +59 -0
  152. voice/stt.py +126 -0
  153. 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
+ )