converse-framework 0.2.0__tar.gz → 0.2.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 (55) hide show
  1. {converse_framework-0.2.0 → converse_framework-0.2.2}/PKG-INFO +34 -2
  2. {converse_framework-0.2.0 → converse_framework-0.2.2}/README.md +33 -1
  3. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/pocket_tts.py +25 -20
  4. {converse_framework-0.2.0 → converse_framework-0.2.2}/pyproject.toml +1 -1
  5. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_providers.py +44 -11
  6. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_session.py +3 -3
  7. {converse_framework-0.2.0 → converse_framework-0.2.2}/.gitattributes +0 -0
  8. {converse_framework-0.2.0 → converse_framework-0.2.2}/.github/workflows/publish.yml +0 -0
  9. {converse_framework-0.2.0 → converse_framework-0.2.2}/.gitignore +0 -0
  10. {converse_framework-0.2.0 → converse_framework-0.2.2}/CHANGELOG.md +0 -0
  11. {converse_framework-0.2.0 → converse_framework-0.2.2}/LICENSE +0 -0
  12. {converse_framework-0.2.0 → converse_framework-0.2.2}/MIGRATION.md +0 -0
  13. {converse_framework-0.2.0 → converse_framework-0.2.2}/benchmarks/perf_compare.py +0 -0
  14. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/__init__.py +0 -0
  15. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/audio_utils.py +0 -0
  16. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/cuda_utils.py +0 -0
  17. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/events.py +0 -0
  18. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/examples/__init__.py +0 -0
  19. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/examples/subprocess_provider.py +0 -0
  20. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/examples/text_chat.py +0 -0
  21. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/examples/voice_chat.py +0 -0
  22. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/examples/websocket_voice_chat.py +0 -0
  23. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/js/browser-voice-client.js +0 -0
  24. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/js/mic-frame-sender.js +0 -0
  25. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/js/speaker-echo-guard.js +0 -0
  26. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/js/tts-audio-player.js +0 -0
  27. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/pipeline.py +0 -0
  28. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/protocols.py +0 -0
  29. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/provider_events.py +0 -0
  30. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/__init__.py +0 -0
  31. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/faster_whisper.py +0 -0
  32. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/kokoro_onnx.py +0 -0
  33. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/llamacpp.py +0 -0
  34. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/mock.py +0 -0
  35. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/silero.py +0 -0
  36. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/unavailable.py +0 -0
  37. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/providers/whisper_cpp.py +0 -0
  38. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/registry.py +0 -0
  39. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/session.py +0 -0
  40. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/transport.py +0 -0
  41. {converse_framework-0.2.0 → converse_framework-0.2.2}/converse_framework/utterance_collector.py +0 -0
  42. {converse_framework-0.2.0 → converse_framework-0.2.2}/plan.md +0 -0
  43. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/js/manual-smoke-test.html +0 -0
  44. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/js/test_helpers.mjs +0 -0
  45. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/js/test_speaker_echo_guard.mjs +0 -0
  46. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_audio_utils.py +0 -0
  47. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_cuda_utils.py +0 -0
  48. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_events.py +0 -0
  49. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_examples.py +0 -0
  50. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_pipeline.py +0 -0
  51. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_protocols.py +0 -0
  52. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_registry.py +0 -0
  53. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_transport.py +0 -0
  54. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_utterance_collector.py +0 -0
  55. {converse_framework-0.2.0 → converse_framework-0.2.2}/tests/test_whisper_cpp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: converse-framework
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Provider-agnostic speech stack for speech-to-speech applications
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -49,6 +49,38 @@ Description-Content-Type: text/markdown
49
49
 
50
50
  Provider-agnostic speech stack for speech-to-speech applications.
51
51
 
52
+ ## Table of Contents
53
+
54
+ - [Install](#install)
55
+ - [Missing dependency behavior](#missing-dependency-behavior)
56
+ - [Python version compatibility](#python-version-compatibility)
57
+ - [Quick Start](#quick-start)
58
+ - [Provider status semantics](#provider-status-semantics)
59
+ - [Recipes](#recipes)
60
+ - [Minimal mock text pipeline](#minimal-mock-text-pipeline)
61
+ - [Audio frame to utterance collector to pipeline](#audio-frame-to-utterance-collector-to-pipeline)
62
+ - [Custom provider registration](#custom-provider-registration)
63
+ - [Custom event sink](#custom-event-sink)
64
+ - [Browser playback](#browser-playback-js-reference-client)
65
+ - [Browser microphone capture](#browser-microphone-capture-js-reference-client)
66
+ - [Mobile browser microphone testing](#mobile-browser-microphone-testing)
67
+ - [Wrap an external CLI as a provider](#wrap-an-external-cli-as-a-provider)
68
+ - [Pocket TTS voice listing and configuration](#pocket-tts-voice-listing-and-configuration)
69
+ - [CUDA DLL helper](#cuda-dll-helper-windows)
70
+ - [Runtime Provider Updates](#runtime-provider-updates)
71
+ - [ProviderBundle.replace()](#providerbundlereplace)
72
+ - [ProviderBundle.unload_replaced()](#providerbundleunload_replaced)
73
+ - [SpeechPipeline.update_providers()](#speechpipelineupdate_providers)
74
+ - [AudioUtteranceCollector.update_vad_provider()](#audioutterancecollectorupdate_vad_provider)
75
+ - [End-to-end pattern](#end-to-end-pattern)
76
+ - [WebSocket Session Helper](#websocket-session-helper)
77
+ - [Examples](#examples)
78
+ - [Text chat](#text-chat-automated-test-covered)
79
+ - [Voice chat](#voice-chat-manual)
80
+ - [Framework / App Boundary](#framework--app-boundary)
81
+ - [Transport boundary](#transport-boundary)
82
+ - [Status](#status)
83
+
52
84
  ## Install
53
85
 
54
86
  ```bash
@@ -644,7 +676,7 @@ for v in voices:
644
676
  Change voice (clears only the voice cache, preserves the loaded model):
645
677
 
646
678
  ```python
647
- result = provider.configure(voice="galileo")
679
+ result = provider.configure(voice="anna")
648
680
  print(result.changed, result.requires_reload)
649
681
  # True, False — model stays, voice state reloaded
650
682
  ```
@@ -2,6 +2,38 @@
2
2
 
3
3
  Provider-agnostic speech stack for speech-to-speech applications.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Install](#install)
8
+ - [Missing dependency behavior](#missing-dependency-behavior)
9
+ - [Python version compatibility](#python-version-compatibility)
10
+ - [Quick Start](#quick-start)
11
+ - [Provider status semantics](#provider-status-semantics)
12
+ - [Recipes](#recipes)
13
+ - [Minimal mock text pipeline](#minimal-mock-text-pipeline)
14
+ - [Audio frame to utterance collector to pipeline](#audio-frame-to-utterance-collector-to-pipeline)
15
+ - [Custom provider registration](#custom-provider-registration)
16
+ - [Custom event sink](#custom-event-sink)
17
+ - [Browser playback](#browser-playback-js-reference-client)
18
+ - [Browser microphone capture](#browser-microphone-capture-js-reference-client)
19
+ - [Mobile browser microphone testing](#mobile-browser-microphone-testing)
20
+ - [Wrap an external CLI as a provider](#wrap-an-external-cli-as-a-provider)
21
+ - [Pocket TTS voice listing and configuration](#pocket-tts-voice-listing-and-configuration)
22
+ - [CUDA DLL helper](#cuda-dll-helper-windows)
23
+ - [Runtime Provider Updates](#runtime-provider-updates)
24
+ - [ProviderBundle.replace()](#providerbundlereplace)
25
+ - [ProviderBundle.unload_replaced()](#providerbundleunload_replaced)
26
+ - [SpeechPipeline.update_providers()](#speechpipelineupdate_providers)
27
+ - [AudioUtteranceCollector.update_vad_provider()](#audioutterancecollectorupdate_vad_provider)
28
+ - [End-to-end pattern](#end-to-end-pattern)
29
+ - [WebSocket Session Helper](#websocket-session-helper)
30
+ - [Examples](#examples)
31
+ - [Text chat](#text-chat-automated-test-covered)
32
+ - [Voice chat](#voice-chat-manual)
33
+ - [Framework / App Boundary](#framework--app-boundary)
34
+ - [Transport boundary](#transport-boundary)
35
+ - [Status](#status)
36
+
5
37
  ## Install
6
38
 
7
39
  ```bash
@@ -597,7 +629,7 @@ for v in voices:
597
629
  Change voice (clears only the voice cache, preserves the loaded model):
598
630
 
599
631
  ```python
600
- result = provider.configure(voice="galileo")
632
+ result = provider.configure(voice="anna")
601
633
  print(result.changed, result.requires_reload)
602
634
  # True, False — model stays, voice state reloaded
603
635
  ```
@@ -40,27 +40,32 @@ class PocketTTSProvider(TTSProvider):
40
40
  # Known voice identifiers for pocket-tts; listed here so status
41
41
  # can advertise them without importing the heavy backend.
42
42
  self._known_voices = (
43
+ {"id": "alba", "label": "Alba", "language": "en"},
44
+ {"id": "giovanni", "label": "Giovanni", "language": "it"},
45
+ {"id": "lola", "label": "Lola", "language": "es"},
46
+ {"id": "juergen", "label": "Juergen", "language": "de"},
47
+ {"id": "rafael", "label": "Rafael", "language": "pt"},
48
+ {"id": "estelle", "label": "Estelle", "language": "fr"},
49
+ {"id": "anna", "label": "Anna", "language": "en"},
43
50
  {"id": "azelma", "label": "Azelma", "language": "en"},
44
- {"id": "bela", "label": "Bela", "language": "en"},
45
- {"id": "conrad", "label": "Conrad", "language": "en"},
46
- {"id": "demeter", "label": "Demeter", "language": "en"},
47
- {"id": "ebenezer", "label": "Ebenezer", "language": "en"},
48
- {"id": "ferdinand", "label": "Ferdinand", "language": "en"},
49
- {"id": "gaspard", "label": "Gaspard", "language": "en"},
50
- {"id": "horace", "label": "Horace", "language": "en"},
51
- {"id": "ivo", "label": "Ivo", "language": "en"},
52
- {"id": "jean", "label": "Jean", "language": "fr"},
53
- {"id": "kimber", "label": "Kimber", "language": "en"},
54
- {"id": "lobelia", "label": "Lobelia", "language": "en"},
55
- {"id": "marie", "label": "Marie", "language": "fr"},
56
- {"id": "nico", "label": "Nico", "language": "en"},
57
- {"id": "orion", "label": "Orion", "language": "en"},
58
- {"id": "pavel", "label": "Pavel", "language": "en"},
59
- {"id": "quito", "label": "Quito", "language": "en"},
60
- {"id": "river", "label": "River", "language": "en"},
61
- {"id": "sophia", "label": "Sophia", "language": "en"},
62
- {"id": "tom", "label": "Tom", "language": "en"},
63
- {"id": "xavier", "label": "Xavier", "language": "en"},
51
+ {"id": "bill_boerst", "label": "Bill Boerst", "language": "en"},
52
+ {"id": "caro_davy", "label": "Caro Davy", "language": "en"},
53
+ {"id": "charles", "label": "Charles", "language": "en"},
54
+ {"id": "cosette", "label": "Cosette", "language": "en"},
55
+ {"id": "eponine", "label": "Eponine", "language": "en"},
56
+ {"id": "eve", "label": "Eve", "language": "en"},
57
+ {"id": "fantine", "label": "Fantine", "language": "en"},
58
+ {"id": "george", "label": "George", "language": "en"},
59
+ {"id": "jane", "label": "Jane", "language": "en"},
60
+ {"id": "jean", "label": "Jean", "language": "en"},
61
+ {"id": "javert", "label": "Javert", "language": "en"},
62
+ {"id": "marius", "label": "Marius", "language": "en"},
63
+ {"id": "mary", "label": "Mary", "language": "en"},
64
+ {"id": "michael", "label": "Michael", "language": "en"},
65
+ {"id": "paul", "label": "Paul", "language": "en"},
66
+ {"id": "peter_yearsley", "label": "Peter Yearsley", "language": "en"},
67
+ {"id": "stuart_bell", "label": "Stuart Bell", "language": "en"},
68
+ {"id": "vera", "label": "Vera", "language": "en"},
64
69
  )
65
70
 
66
71
  @property
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "converse-framework"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Provider-agnostic speech stack for speech-to-speech applications"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -208,8 +208,8 @@ def test_pocket_tts_set_voice_clears_only_voice_state():
208
208
  assert provider._model is not None
209
209
  assert provider._voice_state is not None
210
210
 
211
- result = provider.set_voice("bela")
212
- assert result.active_voice == "bela"
211
+ result = provider.set_voice("anna")
212
+ assert result.active_voice == "anna"
213
213
  # Model stays loaded
214
214
  assert provider._model is not None
215
215
  # Voice state cleared
@@ -282,10 +282,10 @@ def test_pocket_tts_configure_voice_unloads_voice_state():
282
282
  ),
283
283
  )
284
284
 
285
- result = asyncio.run(provider.configure(voice="bela"))
285
+ result = asyncio.run(provider.configure(voice="anna"))
286
286
  assert result.changed is True
287
287
  assert result.requires_reload is True
288
- assert provider.voice == "bela"
288
+ assert provider.voice == "anna"
289
289
  assert provider._model is not None
290
290
  assert provider._voice_state is None
291
291
 
@@ -354,17 +354,50 @@ def test_pocket_tts_list_voices_returns_structured_voice_info():
354
354
  )
355
355
 
356
356
  voices = provider.list_voices()
357
- assert len(voices) >= 3
357
+ expected_ids = [
358
+ "alba",
359
+ "giovanni",
360
+ "lola",
361
+ "juergen",
362
+ "rafael",
363
+ "estelle",
364
+ "anna",
365
+ "azelma",
366
+ "bill_boerst",
367
+ "caro_davy",
368
+ "charles",
369
+ "cosette",
370
+ "eponine",
371
+ "eve",
372
+ "fantine",
373
+ "george",
374
+ "jane",
375
+ "jean",
376
+ "javert",
377
+ "marius",
378
+ "mary",
379
+ "michael",
380
+ "paul",
381
+ "peter_yearsley",
382
+ "stuart_bell",
383
+ "vera",
384
+ ]
385
+ assert [v.id for v in voices] == expected_ids
358
386
  assert isinstance(voices[0], VoiceInfo)
359
- assert voices[0].id == "azelma"
360
- assert voices[0].label == "Azelma"
387
+ assert voices[0].id == "alba"
388
+ assert voices[0].label == "Alba"
361
389
 
362
390
  # Find a voice by id
363
391
  by_id = {v.id: v for v in voices}
364
- assert "bela" in by_id
365
- assert by_id["bela"].language == "en"
366
- # French voices
367
- assert by_id["jean"].language == "fr"
392
+ assert "anna" in by_id
393
+ assert by_id["anna"].language == "en"
394
+ # Multilingual voices
395
+ assert by_id["estelle"].language == "fr"
396
+ assert by_id["giovanni"].language == "it"
397
+ assert by_id["lola"].language == "es"
398
+ assert by_id["juergen"].language == "de"
399
+ assert by_id["rafael"].language == "pt"
400
+ assert by_id["jean"].language == "en"
368
401
  assert by_id["jean"].gender == "neutral"
369
402
 
370
403
 
@@ -226,10 +226,10 @@ async def test_settings_update_routes_to_hook():
226
226
  session = WebSocketSession(transport, hooks=hooks) # type: ignore[arg-type]
227
227
 
228
228
  await session.handle_message(
229
- {"type": "settings.update", "payload": {"voice": "bela"}}
229
+ {"type": "settings.update", "payload": {"voice": "anna"}}
230
230
  )
231
231
  assert len(settings_log) == 1
232
- assert settings_log[0] == {"voice": "bela"}
232
+ assert settings_log[0] == {"voice": "anna"}
233
233
 
234
234
 
235
235
  @pytest.mark.asyncio
@@ -238,7 +238,7 @@ async def test_settings_update_no_hook_is_silent():
238
238
  session = WebSocketSession(transport) # type: ignore[arg-type]
239
239
 
240
240
  await session.handle_message(
241
- {"type": "settings.update", "payload": {"voice": "bela"}}
241
+ {"type": "settings.update", "payload": {"voice": "anna"}}
242
242
  )
243
243
  # No events — session silently ignores unhandled settings
244
244
  assert len(transport.sent) == 0