abstractvoice 0.6.1__tar.gz → 0.6.2__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.
Files changed (84) hide show
  1. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/PKG-INFO +1 -1
  2. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/__init__.py +1 -1
  3. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/examples/cli_repl.py +6 -1
  4. abstractvoice-0.6.2/abstractvoice/text_sanitize.py +33 -0
  5. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/PKG-INFO +1 -1
  6. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/SOURCES.txt +2 -0
  7. abstractvoice-0.6.2/tests/test_text_sanitize_markdown.py +29 -0
  8. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/LICENSE +0 -0
  9. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/README.md +0 -0
  10. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/__main__.py +0 -0
  11. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/adapters/__init__.py +0 -0
  12. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/adapters/base.py +0 -0
  13. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/adapters/stt_faster_whisper.py +0 -0
  14. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/adapters/tts_piper.py +0 -0
  15. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/aec/__init__.py +0 -0
  16. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/aec/webrtc_apm.py +0 -0
  17. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/artifacts.py +0 -0
  18. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/audio/__init__.py +0 -0
  19. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/audio/recorder.py +0 -0
  20. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/audio/resample.py +0 -0
  21. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/cloning/__init__.py +0 -0
  22. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/cloning/engine_chroma.py +0 -0
  23. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/cloning/engine_f5.py +0 -0
  24. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/cloning/manager.py +0 -0
  25. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/cloning/store.py +0 -0
  26. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/compute/__init__.py +0 -0
  27. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/compute/device.py +0 -0
  28. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/config/__init__.py +0 -0
  29. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/config/voice_catalog.py +0 -0
  30. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/dependency_check.py +0 -0
  31. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/examples/__init__.py +0 -0
  32. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/examples/voice_cli.py +0 -0
  33. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/examples/web_api.py +0 -0
  34. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/integrations/__init__.py +0 -0
  35. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/integrations/abstractcore.py +0 -0
  36. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/integrations/abstractcore_plugin.py +0 -0
  37. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/prefetch.py +0 -0
  38. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/recognition.py +0 -0
  39. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/stop_phrase.py +0 -0
  40. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/stt/__init__.py +0 -0
  41. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/stt/transcriber.py +0 -0
  42. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/tts/__init__.py +0 -0
  43. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/tts/adapter_tts_engine.py +0 -0
  44. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/tts/tts_engine.py +0 -0
  45. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vad/__init__.py +0 -0
  46. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vad/voice_detector.py +0 -0
  47. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/__init__.py +0 -0
  48. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/common.py +0 -0
  49. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/core.py +0 -0
  50. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/manager.py +0 -0
  51. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/stt_mixin.py +0 -0
  52. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/vm/tts_mixin.py +0 -0
  53. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice/voice_manager.py +0 -0
  54. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/dependency_links.txt +0 -0
  55. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/entry_points.txt +0 -0
  56. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/requires.txt +0 -0
  57. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/abstractvoice.egg-info/top_level.txt +0 -0
  58. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/pyproject.toml +0 -0
  59. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/setup.cfg +0 -0
  60. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_abstractcore_plugin.py +0 -0
  61. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_adr0002_phase1.py +0 -0
  62. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_adr0002_phase2_optional_aec.py +0 -0
  63. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_artifact_tools.py +0 -0
  64. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_audio_player_resample.py +0 -0
  65. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_callbacks.py +0 -0
  66. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_chroma_cloning_integration.py +0 -0
  67. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_cloned_tts_cancellation.py +0 -0
  68. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_cloning_reference_text_autofallback.py +0 -0
  69. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_faster_whisper_adapter.py +0 -0
  70. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_fresh_install.py +0 -0
  71. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_full_mode_echo_gate.py +0 -0
  72. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_piper_adapter.py +0 -0
  73. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_reference_text_autofallback_offline_cached_model.py +0 -0
  74. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_repl_interrupt_cloned_voice_does_not_resume_old_audio.py +0 -0
  75. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_stop_phrase_continuous_detector.py +0 -0
  76. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_stop_phrase_matching_is_tolerant.py +0 -0
  77. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_stop_speaking_restores_recognizer_state.py +0 -0
  78. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_clone_store_delete_rename.py +0 -0
  79. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_cloner_engine_dispatch.py +0 -0
  80. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_cloning_integration_hal9000.py +0 -0
  81. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_cloning_store.py +0 -0
  82. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_mode_wait_pauses_listening_during_tts.py +0 -0
  83. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_recognizer_ptt_profile.py +0 -0
  84. {abstractvoice-0.6.1 → abstractvoice-0.6.2}/tests/test_voice_switching.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractvoice
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: A modular Python library for voice interactions with AI systems
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  License-Expression: MIT
@@ -29,5 +29,5 @@ warnings.filterwarnings(
29
29
  # Import the main class for public API
30
30
  from .voice_manager import VoiceManager
31
31
 
32
- __version__ = "0.6.1"
32
+ __version__ = "0.6.2"
33
33
  __all__ = ["VoiceManager"]
@@ -19,6 +19,7 @@ import threading
19
19
  import time
20
20
  import requests
21
21
  from abstractvoice import VoiceManager
22
+ from abstractvoice.text_sanitize import sanitize_markdown_for_speech
22
23
 
23
24
 
24
25
  # ANSI color codes
@@ -1563,6 +1564,10 @@ class VoiceREPL(cmd.Cmd):
1563
1564
  if not self.voice_manager:
1564
1565
  return
1565
1566
 
1567
+ # LLM output often contains Markdown. Strip the most common formatting
1568
+ # tokens so TTS stays natural (do not change what is printed).
1569
+ speak_text = sanitize_markdown_for_speech(text)
1570
+
1566
1571
  is_clone = bool(self.current_tts_voice)
1567
1572
  if not is_clone:
1568
1573
  # Offline-first: Piper voices must be explicitly cached. Provide a clear
@@ -1583,7 +1588,7 @@ class VoiceREPL(cmd.Cmd):
1583
1588
  try:
1584
1589
  if is_clone:
1585
1590
  ind.start()
1586
- self.voice_manager.speak(text, voice=self.current_tts_voice)
1591
+ self.voice_manager.speak(speak_text, voice=self.current_tts_voice)
1587
1592
 
1588
1593
  if not is_clone:
1589
1594
  return
@@ -0,0 +1,33 @@
1
+ """Lightweight text sanitizers for better speech UX.
2
+
3
+ Design goals:
4
+ - No heavy dependencies (regex only).
5
+ - Deterministic and conservative (only strip the most common Markdown syntax).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+
12
+
13
+ _MD_HEADER_RE = re.compile(r"(?m)^[ \t]*#{1,5}(?!#)[ \t]*(\S.*)$")
14
+ _MD_BOLD_RE = re.compile(r"\*\*([^*\n]+?)\*\*")
15
+ # Avoid matching "**bold**" as italic. The bold pass runs first, but keep this safe.
16
+ _MD_ITALIC_RE = re.compile(r"(?<!\*)\*([^*\n]+?)\*(?!\*)")
17
+
18
+
19
+ def sanitize_markdown_for_speech(text: str) -> str:
20
+ """Remove common Markdown syntax that sounds bad in TTS.
21
+
22
+ Intentionally minimal:
23
+ - Strip ATX headers at the start of a line: "#", "##", ... "#####"
24
+ - Strip emphasis markers: "**bold**" and "*italic*"
25
+ """
26
+ if not text:
27
+ return ""
28
+
29
+ out = str(text)
30
+ out = _MD_HEADER_RE.sub(r"\1", out)
31
+ out = _MD_BOLD_RE.sub(r"\1", out)
32
+ out = _MD_ITALIC_RE.sub(r"\1", out)
33
+ return out
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractvoice
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: A modular Python library for voice interactions with AI systems
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  License-Expression: MIT
@@ -8,6 +8,7 @@ abstractvoice/dependency_check.py
8
8
  abstractvoice/prefetch.py
9
9
  abstractvoice/recognition.py
10
10
  abstractvoice/stop_phrase.py
11
+ abstractvoice/text_sanitize.py
11
12
  abstractvoice/voice_manager.py
12
13
  abstractvoice.egg-info/PKG-INFO
13
14
  abstractvoice.egg-info/SOURCES.txt
@@ -71,6 +72,7 @@ tests/test_repl_interrupt_cloned_voice_does_not_resume_old_audio.py
71
72
  tests/test_stop_phrase_continuous_detector.py
72
73
  tests/test_stop_phrase_matching_is_tolerant.py
73
74
  tests/test_stop_speaking_restores_recognizer_state.py
75
+ tests/test_text_sanitize_markdown.py
74
76
  tests/test_voice_clone_store_delete_rename.py
75
77
  tests/test_voice_cloner_engine_dispatch.py
76
78
  tests/test_voice_cloning_integration_hal9000.py
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from abstractvoice.text_sanitize import sanitize_markdown_for_speech
4
+
5
+
6
+ def test_sanitize_removes_bold_single_word() -> None:
7
+ assert sanitize_markdown_for_speech("Hello **world**!") == "Hello world!"
8
+
9
+
10
+ def test_sanitize_removes_bold_multiple_words() -> None:
11
+ assert sanitize_markdown_for_speech("**sentence with multiple words**") == "sentence with multiple words"
12
+
13
+
14
+ def test_sanitize_removes_italic_single_word() -> None:
15
+ assert sanitize_markdown_for_speech("Use *italics* here.") == "Use italics here."
16
+
17
+
18
+ def test_sanitize_removes_italic_multiple_words() -> None:
19
+ assert sanitize_markdown_for_speech("Use *a sentence with words* here.") == "Use a sentence with words here."
20
+
21
+
22
+ def test_sanitize_strips_markdown_headers_keep_text() -> None:
23
+ txt = "# Title\n## Subtitle\n##### Small header\nNot a header\n###"
24
+ assert sanitize_markdown_for_speech(txt) == "Title\nSubtitle\nSmall header\nNot a header\n###"
25
+
26
+
27
+ def test_sanitize_does_not_touch_bullet_marker_without_closing_star() -> None:
28
+ assert sanitize_markdown_for_speech("* item") == "* item"
29
+
File without changes
File without changes
File without changes