pipecat-oogam 0.1.0__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.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .env
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-11-12
4
+
5
+ First release. Tested with Pipecat v1.16.0.
6
+
7
+ - `OogamSTTService` — streaming speech-to-text over
8
+ `wss://platform.oogam.ai/v1/realtime?intent=transcription`. Sends `finalize`
9
+ when Pipecat's VAD reports the user stopped speaking, and Oogam confirms it,
10
+ so transcripts are marked finalized for Pipecat's TTFB metrics. Optional
11
+ `auto_switch` follows a caller who changes language (`on_language_changed`
12
+ event). Language changes through `STTUpdateSettingsFrame` apply live, without
13
+ reconnecting.
14
+ - `OogamTTSService` — streaming text-to-speech from `POST /v1/audio/speech`
15
+ (`stream: true`, `response_format: "pcm"`, 24 kHz).
16
+ - `OogamLLMService` — Sutra (`sutra-conversation`) through Pipecat's OpenAI
17
+ service. Function calling is not supported by the endpoint yet.
18
+ - Language helpers mapping Pipecat `Language` values to Oogam's realtime and
19
+ speech codes.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dwet Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.5
2
+ Name: pipecat-oogam
3
+ Version: 0.1.0
4
+ Summary: Oogam speech services for Pipecat — streaming STT with client-driven turn finalize, streaming TTS, and Sutra — for 27 Indian languages and English.
5
+ Project-URL: Homepage, https://oogam.ai/integrations/pipecat
6
+ Project-URL: Documentation, https://oogam.ai/docs/streaming-stt
7
+ Author-email: Dwet Labs <support@dwetlabs.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: indian-languages,naad,oogam,pipecat,stt,sutra,tts,voice-agent
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: aiohttp>=3.9
20
+ Requires-Dist: pipecat-ai<2,>=1.16
21
+ Requires-Dist: websockets>=13
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # pipecat-oogam
28
+
29
+ Oogam speech services for [Pipecat](https://github.com/pipecat-ai/pipecat):
30
+ streaming speech-to-text, streaming text-to-speech and the Sutra conversation
31
+ model, for 27 Indian languages and English.
32
+
33
+ | Service | Oogam endpoint | What it does |
34
+ |---|---|---|
35
+ | `OogamSTTService` | `wss://platform.oogam.ai/v1/realtime?intent=transcription` | Streaming STT. Final transcripts; finalizes the turn the moment Pipecat's VAD says the user stopped. |
36
+ | `OogamTTSService` | `POST /v1/audio/speech` (`stream: true`, `response_format: "pcm"`) | Streaming TTS — 24 kHz PCM played as it is generated. |
37
+ | `OogamLLMService` | `POST /v1/chat/completions` | Sutra (`sutra-conversation`) through Pipecat's OpenAI service. |
38
+
39
+ ```bash
40
+ pip install pipecat-oogam
41
+ ```
42
+
43
+ Requires Python 3.11+, Pipecat 1.16 or later (1.x) and an Oogam API key
44
+ with the NAAD product enabled. **Tested with Pipecat v1.16.0.**
45
+
46
+ Maintained by [DWET Labs](https://oogam.ai), the team behind Oogam. Integration
47
+ guide: [oogam.ai/integrations/pipecat](https://oogam.ai/integrations/pipecat).
48
+
49
+ ## Speech-to-text
50
+
51
+ ```python
52
+ from pipecat.transcriptions.language import Language
53
+ from pipecat_oogam import OogamSTTService
54
+
55
+ stt = OogamSTTService(
56
+ api_key=os.environ["OOGAM_API_KEY"],
57
+ settings=OogamSTTService.Settings(language=Language.HI),
58
+ )
59
+ ```
60
+
61
+ **Turn-taking stays with your pipeline.** Oogam's own end-of-turn detection
62
+ waits for 700 ms of silence. With this service you do not wait for it: when
63
+ Pipecat's VAD reports that the user stopped speaking, the service sends Oogam
64
+ `finalize` and Oogam closes the turn straight away. Smart Turn — Pipecat's
65
+ default — or any other turn strategy then decides whether the user's turn is
66
+ really over, exactly as with any other streaming STT. Set
67
+ `finalize_on_vad_stop=False` only if you want Oogam to decide instead.
68
+
69
+ Each transcript is final. Measured on the live engine (12 Nov 2026): with
70
+ `finalize` the transcript arrives about 0.33 s after the caller stops, against
71
+ about 0.8 s when Oogam waits out its own silence window. If a caller pauses for
72
+ 0.8 s or more mid-sentence, Oogam closes a turn there and the rest arrives as a
73
+ second transcript; Pipecat's user aggregator joins them, so Smart Turn still
74
+ decides when the agent replies. (If Oogam ever re-transcribes a turn with
75
+ `replace`, the service pushes only the new words.)
76
+
77
+ **The language must be given** — Oogam does not auto-detect. To follow a caller
78
+ who switches language mid-call, turn on auto-switch:
79
+
80
+ ```python
81
+ stt = OogamSTTService(
82
+ api_key=os.environ["OOGAM_API_KEY"],
83
+ settings=OogamSTTService.Settings(
84
+ language=Language.HI,
85
+ auto_switch=True,
86
+ candidates=[Language.HI, Language.EN_IN, Language.GU],
87
+ ),
88
+ )
89
+
90
+ @stt.event_handler("on_language_changed")
91
+ async def on_language_changed(service, from_code, to_code, reason):
92
+ # e.g. switch the TTS language to match
93
+ await task.queue_frame(TTSUpdateSettingsFrame(delta=OogamTTSService.Settings(language=to_code)))
94
+ ```
95
+
96
+ Auto-switch acts on Oogam's language hint, which comes after the turn it was
97
+ detected in — so the first sentence after a switch is transcribed in the old
98
+ language, and the switch applies from the next one. Changing `language` with
99
+ an `STTUpdateSettingsFrame` re-pins the live session without reconnecting.
100
+
101
+ | Setting | Default | |
102
+ |---|---|---|
103
+ | `language` | `Language.HI` | A Pipecat `Language` or an Oogam short code (`hi`, `en-IN`, `bho`). |
104
+ | `vad_end_silence_ms` | Oogam's (700) | 700–5000. Reserved: sent to Oogam, but the current engine does not act on it. |
105
+ | `auto_switch` | `False` | Follow a caller who changes language. |
106
+ | `candidates` | any | Languages a call may switch to. |
107
+ | `min_confidence` | 0.6 | Hint confidence needed to switch. |
108
+
109
+ Audio is sent at 16 kHz mono PCM16; Pipecat resamples whatever your
110
+ transport delivers.
111
+
112
+ ## Text-to-speech
113
+
114
+ ```python
115
+ async with aiohttp.ClientSession() as session:
116
+ tts = OogamTTSService(
117
+ api_key=os.environ["OOGAM_API_KEY"],
118
+ aiohttp_session=session,
119
+ settings=OogamTTSService.Settings(voice="aditi", language=Language.HI),
120
+ )
121
+ ```
122
+
123
+ `voice` is a preset id (`aditi`, `arjun`, …) or a cloned voice id from
124
+ `GET /v1/voices`. Set `language` deliberately: Oogam falls back to Hindi for a
125
+ code it does not know.
126
+
127
+ ## Sutra
128
+
129
+ ```python
130
+ llm = OogamLLMService(api_key=os.environ["OOGAM_API_KEY"])
131
+ ```
132
+
133
+ **No function calling yet.** `/v1/chat/completions` answers a request that
134
+ carries `tools` with `400 tools_not_supported`, so do not register tools on
135
+ this service. For tool use, keep Oogam for STT and TTS and pair them with an
136
+ LLM service that supports tools.
137
+
138
+ ## A complete agent
139
+
140
+ `examples/voice_agent.py` (in the source distribution, and on
141
+ [oogam.ai/integrations/pipecat](https://oogam.ai/integrations/pipecat#quickstart))
142
+ runs Oogam STT → Sutra → Oogam TTS with Silero VAD and Smart Turn, in the
143
+ browser through Pipecat's development runner:
144
+
145
+ ```bash
146
+ pip install "pipecat-ai[runner,webrtc,silero,local-smart-turn-v3]" pipecat-oogam
147
+ OOGAM_API_KEY=sk-setu-… python examples/voice_agent.py
148
+ ```
149
+
150
+ ## Billing
151
+
152
+ Streaming STT is billed at the speech-to-text rate per second the socket is
153
+ open, TTS per character as on the REST API, and Sutra per token.
154
+
155
+ ## Developing
156
+
157
+ ```bash
158
+ pip install -e ".[dev]"
159
+ pytest
160
+ ```
161
+
162
+ Tests that need a running Oogam gateway are skipped unless `OOGAM_DEV_STACK`
163
+ points at one.
164
+
165
+ ## Support
166
+
167
+ [support@dwetlabs.com](mailto:support@dwetlabs.com) ·
168
+ [Streaming STT reference](https://oogam.ai/docs/streaming-stt)
@@ -0,0 +1,142 @@
1
+ # pipecat-oogam
2
+
3
+ Oogam speech services for [Pipecat](https://github.com/pipecat-ai/pipecat):
4
+ streaming speech-to-text, streaming text-to-speech and the Sutra conversation
5
+ model, for 27 Indian languages and English.
6
+
7
+ | Service | Oogam endpoint | What it does |
8
+ |---|---|---|
9
+ | `OogamSTTService` | `wss://platform.oogam.ai/v1/realtime?intent=transcription` | Streaming STT. Final transcripts; finalizes the turn the moment Pipecat's VAD says the user stopped. |
10
+ | `OogamTTSService` | `POST /v1/audio/speech` (`stream: true`, `response_format: "pcm"`) | Streaming TTS — 24 kHz PCM played as it is generated. |
11
+ | `OogamLLMService` | `POST /v1/chat/completions` | Sutra (`sutra-conversation`) through Pipecat's OpenAI service. |
12
+
13
+ ```bash
14
+ pip install pipecat-oogam
15
+ ```
16
+
17
+ Requires Python 3.11+, Pipecat 1.16 or later (1.x) and an Oogam API key
18
+ with the NAAD product enabled. **Tested with Pipecat v1.16.0.**
19
+
20
+ Maintained by [DWET Labs](https://oogam.ai), the team behind Oogam. Integration
21
+ guide: [oogam.ai/integrations/pipecat](https://oogam.ai/integrations/pipecat).
22
+
23
+ ## Speech-to-text
24
+
25
+ ```python
26
+ from pipecat.transcriptions.language import Language
27
+ from pipecat_oogam import OogamSTTService
28
+
29
+ stt = OogamSTTService(
30
+ api_key=os.environ["OOGAM_API_KEY"],
31
+ settings=OogamSTTService.Settings(language=Language.HI),
32
+ )
33
+ ```
34
+
35
+ **Turn-taking stays with your pipeline.** Oogam's own end-of-turn detection
36
+ waits for 700 ms of silence. With this service you do not wait for it: when
37
+ Pipecat's VAD reports that the user stopped speaking, the service sends Oogam
38
+ `finalize` and Oogam closes the turn straight away. Smart Turn — Pipecat's
39
+ default — or any other turn strategy then decides whether the user's turn is
40
+ really over, exactly as with any other streaming STT. Set
41
+ `finalize_on_vad_stop=False` only if you want Oogam to decide instead.
42
+
43
+ Each transcript is final. Measured on the live engine (12 Nov 2026): with
44
+ `finalize` the transcript arrives about 0.33 s after the caller stops, against
45
+ about 0.8 s when Oogam waits out its own silence window. If a caller pauses for
46
+ 0.8 s or more mid-sentence, Oogam closes a turn there and the rest arrives as a
47
+ second transcript; Pipecat's user aggregator joins them, so Smart Turn still
48
+ decides when the agent replies. (If Oogam ever re-transcribes a turn with
49
+ `replace`, the service pushes only the new words.)
50
+
51
+ **The language must be given** — Oogam does not auto-detect. To follow a caller
52
+ who switches language mid-call, turn on auto-switch:
53
+
54
+ ```python
55
+ stt = OogamSTTService(
56
+ api_key=os.environ["OOGAM_API_KEY"],
57
+ settings=OogamSTTService.Settings(
58
+ language=Language.HI,
59
+ auto_switch=True,
60
+ candidates=[Language.HI, Language.EN_IN, Language.GU],
61
+ ),
62
+ )
63
+
64
+ @stt.event_handler("on_language_changed")
65
+ async def on_language_changed(service, from_code, to_code, reason):
66
+ # e.g. switch the TTS language to match
67
+ await task.queue_frame(TTSUpdateSettingsFrame(delta=OogamTTSService.Settings(language=to_code)))
68
+ ```
69
+
70
+ Auto-switch acts on Oogam's language hint, which comes after the turn it was
71
+ detected in — so the first sentence after a switch is transcribed in the old
72
+ language, and the switch applies from the next one. Changing `language` with
73
+ an `STTUpdateSettingsFrame` re-pins the live session without reconnecting.
74
+
75
+ | Setting | Default | |
76
+ |---|---|---|
77
+ | `language` | `Language.HI` | A Pipecat `Language` or an Oogam short code (`hi`, `en-IN`, `bho`). |
78
+ | `vad_end_silence_ms` | Oogam's (700) | 700–5000. Reserved: sent to Oogam, but the current engine does not act on it. |
79
+ | `auto_switch` | `False` | Follow a caller who changes language. |
80
+ | `candidates` | any | Languages a call may switch to. |
81
+ | `min_confidence` | 0.6 | Hint confidence needed to switch. |
82
+
83
+ Audio is sent at 16 kHz mono PCM16; Pipecat resamples whatever your
84
+ transport delivers.
85
+
86
+ ## Text-to-speech
87
+
88
+ ```python
89
+ async with aiohttp.ClientSession() as session:
90
+ tts = OogamTTSService(
91
+ api_key=os.environ["OOGAM_API_KEY"],
92
+ aiohttp_session=session,
93
+ settings=OogamTTSService.Settings(voice="aditi", language=Language.HI),
94
+ )
95
+ ```
96
+
97
+ `voice` is a preset id (`aditi`, `arjun`, …) or a cloned voice id from
98
+ `GET /v1/voices`. Set `language` deliberately: Oogam falls back to Hindi for a
99
+ code it does not know.
100
+
101
+ ## Sutra
102
+
103
+ ```python
104
+ llm = OogamLLMService(api_key=os.environ["OOGAM_API_KEY"])
105
+ ```
106
+
107
+ **No function calling yet.** `/v1/chat/completions` answers a request that
108
+ carries `tools` with `400 tools_not_supported`, so do not register tools on
109
+ this service. For tool use, keep Oogam for STT and TTS and pair them with an
110
+ LLM service that supports tools.
111
+
112
+ ## A complete agent
113
+
114
+ `examples/voice_agent.py` (in the source distribution, and on
115
+ [oogam.ai/integrations/pipecat](https://oogam.ai/integrations/pipecat#quickstart))
116
+ runs Oogam STT → Sutra → Oogam TTS with Silero VAD and Smart Turn, in the
117
+ browser through Pipecat's development runner:
118
+
119
+ ```bash
120
+ pip install "pipecat-ai[runner,webrtc,silero,local-smart-turn-v3]" pipecat-oogam
121
+ OOGAM_API_KEY=sk-setu-… python examples/voice_agent.py
122
+ ```
123
+
124
+ ## Billing
125
+
126
+ Streaming STT is billed at the speech-to-text rate per second the socket is
127
+ open, TTS per character as on the REST API, and Sutra per token.
128
+
129
+ ## Developing
130
+
131
+ ```bash
132
+ pip install -e ".[dev]"
133
+ pytest
134
+ ```
135
+
136
+ Tests that need a running Oogam gateway are skipped unless `OOGAM_DEV_STACK`
137
+ points at one.
138
+
139
+ ## Support
140
+
141
+ [support@dwetlabs.com](mailto:support@dwetlabs.com) ·
142
+ [Streaming STT reference](https://oogam.ai/docs/streaming-stt)
@@ -0,0 +1,119 @@
1
+ """A Hindi voice agent on Oogam: Oogam STT → Sutra → Oogam TTS.
2
+
3
+ Silero VAD and Smart Turn (Pipecat's default turn detector) decide when the
4
+ caller has finished; Oogam STT finalizes each turn the moment the VAD hears
5
+ the caller stop, so there is no extra wait for Oogam's own end-of-turn
6
+ silence. If the caller switches language, the session follows and the agent
7
+ answers in the new language.
8
+
9
+ pip install "pipecat-ai[runner,webrtc,silero,local-smart-turn-v3]" pipecat-oogam
10
+ OOGAM_API_KEY=sk-setu-… python examples/voice_agent.py
11
+
12
+ Then open the URL it prints and talk.
13
+ """
14
+
15
+ import os
16
+
17
+ import aiohttp
18
+ from loguru import logger
19
+ from pipecat.audio.vad.silero import SileroVADAnalyzer
20
+ from pipecat.frames.frames import LLMRunFrame, TTSUpdateSettingsFrame
21
+ from pipecat.pipeline.pipeline import Pipeline
22
+ from pipecat.pipeline.runner import PipelineRunner
23
+ from pipecat.pipeline.worker import PipelineParams, PipelineWorker
24
+ from pipecat.processors.aggregators.llm_context import LLMContext
25
+ from pipecat.processors.aggregators.llm_response_universal import (
26
+ LLMContextAggregatorPair,
27
+ LLMUserAggregatorParams,
28
+ )
29
+ from pipecat.runner.types import RunnerArguments
30
+ from pipecat.runner.utils import create_transport
31
+ from pipecat.transcriptions.language import Language
32
+ from pipecat.transports.base_transport import TransportParams
33
+
34
+ from pipecat_oogam import OogamLLMService, OogamSTTService, OogamTTSService, to_tts_code
35
+
36
+ SYSTEM_PROMPT = (
37
+ "You are a helpful voice assistant for an Indian bank. Answer in the language "
38
+ "the caller is speaking, in one or two short spoken sentences. No lists, no "
39
+ "markdown — everything you write is read aloud."
40
+ )
41
+
42
+
43
+ async def run_bot(transport, runner_args: RunnerArguments):
44
+ api_key = os.environ["OOGAM_API_KEY"]
45
+
46
+ async with aiohttp.ClientSession() as session:
47
+ stt = OogamSTTService(
48
+ api_key=api_key,
49
+ settings=OogamSTTService.Settings(
50
+ language=Language.HI,
51
+ # Follow a caller who switches between these mid-call.
52
+ auto_switch=True,
53
+ candidates=[Language.HI, Language.EN_IN, Language.GU],
54
+ ),
55
+ )
56
+ llm = OogamLLMService(api_key=api_key)
57
+ tts = OogamTTSService(
58
+ api_key=api_key,
59
+ aiohttp_session=session,
60
+ settings=OogamTTSService.Settings(voice="aditi", language=Language.HI),
61
+ )
62
+
63
+ context = LLMContext([{"role": "system", "content": SYSTEM_PROMPT}])
64
+ aggregators = LLMContextAggregatorPair(
65
+ context,
66
+ # Smart Turn v3 is the default stop strategy; the VAD feeds it.
67
+ user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
68
+ )
69
+
70
+ pipeline = Pipeline(
71
+ [
72
+ transport.input(),
73
+ stt,
74
+ aggregators.user(),
75
+ llm,
76
+ tts,
77
+ transport.output(),
78
+ aggregators.assistant(),
79
+ ]
80
+ )
81
+ worker = PipelineWorker(
82
+ pipeline,
83
+ params=PipelineParams(enable_metrics=True, enable_usage_metrics=True),
84
+ idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
85
+ )
86
+
87
+ @stt.event_handler("on_language_changed")
88
+ async def on_language_changed(service, from_code, to_code, reason):
89
+ # Speak the language the caller switched to.
90
+ logger.info(f"caller switched {from_code} -> {to_code}")
91
+ await worker.queue_frame(
92
+ TTSUpdateSettingsFrame(delta=OogamTTSService.Settings(language=to_tts_code(to_code)))
93
+ )
94
+
95
+ @transport.event_handler("on_client_connected")
96
+ async def on_client_connected(transport, client):
97
+ context.add_message({"role": "user", "content": "Greet the caller briefly in Hindi."})
98
+ await worker.queue_frames([LLMRunFrame()])
99
+
100
+ @transport.event_handler("on_client_disconnected")
101
+ async def on_client_disconnected(transport, client):
102
+ await worker.cancel()
103
+
104
+ runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
105
+ await runner.run(worker)
106
+
107
+
108
+ async def bot(runner_args: RunnerArguments):
109
+ transport = await create_transport(
110
+ runner_args,
111
+ {"webrtc": lambda: TransportParams(audio_in_enabled=True, audio_out_enabled=True)},
112
+ )
113
+ await run_bot(transport, runner_args)
114
+
115
+
116
+ if __name__ == "__main__":
117
+ from pipecat.runner.run import main
118
+
119
+ main()
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pipecat-oogam"
7
+ dynamic = ["version"]
8
+ description = "Oogam speech services for Pipecat — streaming STT with client-driven turn finalize, streaming TTS, and Sutra — for 27 Indian languages and English."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Dwet Labs", email = "support@dwetlabs.com" }]
13
+ keywords = ["pipecat", "oogam", "naad", "sutra", "stt", "tts", "voice-agent", "indian-languages"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
22
+ ]
23
+ # Built and tested against Pipecat 1.16. The 2.0 line has announced removals,
24
+ # so it is excluded until it is tested.
25
+ dependencies = ["pipecat-ai>=1.16,<2", "aiohttp>=3.9", "websockets>=13"]
26
+
27
+ [project.optional-dependencies]
28
+ dev = ["pytest>=8", "pytest-asyncio>=0.23"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://oogam.ai/integrations/pipecat"
32
+ Documentation = "https://oogam.ai/docs/streaming-stt"
33
+
34
+ [tool.hatch.version]
35
+ path = "src/pipecat_oogam/_version.py"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/pipecat_oogam"]
39
+
40
+ # Public source distribution: the package, its unit tests and the example
41
+ # agent only. The internal Voice Lab and the live end-to-end check stay out.
42
+ [tool.hatch.build.targets.sdist]
43
+ only-include = [
44
+ "src",
45
+ "tests/test_languages.py",
46
+ "tests/test_llm.py",
47
+ "tests/test_stt.py",
48
+ "tests/test_tts.py",
49
+ "examples/voice_agent.py",
50
+ "README.md",
51
+ "CHANGELOG.md",
52
+ "LICENSE",
53
+ ]
54
+
55
+ [tool.pytest.ini_options]
56
+ asyncio_mode = "auto"
57
+ asyncio_default_fixture_loop_scope = "function"
58
+ testpaths = ["tests"]
@@ -0,0 +1,25 @@
1
+ """Oogam speech services for Pipecat: streaming STT, streaming TTS and Sutra."""
2
+
3
+ from pipecat_oogam._version import __version__
4
+ from pipecat_oogam.languages import (
5
+ SUPPORTED_REALTIME_CODES,
6
+ from_realtime_code,
7
+ to_realtime_code,
8
+ to_tts_code,
9
+ )
10
+ from pipecat_oogam.llm import OogamLLMService
11
+ from pipecat_oogam.stt import OogamSTTService, OogamSTTSettings
12
+ from pipecat_oogam.tts import OogamTTSService, OogamTTSSettings
13
+
14
+ __all__ = [
15
+ "__version__",
16
+ "OogamLLMService",
17
+ "OogamSTTService",
18
+ "OogamSTTSettings",
19
+ "OogamTTSService",
20
+ "OogamTTSSettings",
21
+ "SUPPORTED_REALTIME_CODES",
22
+ "from_realtime_code",
23
+ "to_realtime_code",
24
+ "to_tts_code",
25
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,109 @@
1
+ """Language codes for Oogam.
2
+
3
+ Oogam uses two code systems, and the same language has a code in each:
4
+
5
+ * the realtime socket (streaming STT) takes short codes — ``hi``, ``gu``,
6
+ ``en-IN`` — the same ones as Live talk;
7
+ * the REST speech endpoints (TTS) take 3-letter codes — ``HIN``, ``GUJ``,
8
+ ``ENG``.
9
+
10
+ Indian English is ``en-IN`` on the socket and ``ENG`` for speech. Plain
11
+ ``en`` on the socket is American English.
12
+
13
+ The table mirrors ``setu-platform/src/constants/languages.ts``. Languages that
14
+ Pipecat's ``Language`` enum does not have (Bhojpuri, Chhattisgarhi, Marwadi,
15
+ Haryanvi, Magahi, Gujarati Kathiyawadi) can be passed as their short code
16
+ string.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from pipecat.transcriptions.language import Language
22
+
23
+ # realtime short code -> TTS 3-letter code
24
+ _LANGUAGES: dict[str, str] = {
25
+ "hi": "HIN",
26
+ "en-IN": "ENG",
27
+ "en": "ENG",
28
+ "gu": "GUJ",
29
+ "pa": "PUN",
30
+ "ne": "NEP",
31
+ "mr": "MAR",
32
+ "ta": "TAM",
33
+ "te": "TEL",
34
+ "kn": "KAN",
35
+ "ml": "MAL",
36
+ "bn": "BEN",
37
+ "ur": "URD",
38
+ "or": "ODI",
39
+ "sd": "SND",
40
+ "kok": "KON",
41
+ "as": "ASS",
42
+ "bho": "BHO",
43
+ "cha": "CHA",
44
+ "mai": "MAI",
45
+ "doi": "DOG",
46
+ "mwr": "MWR",
47
+ "bgc": "HAR",
48
+ "sa": "SAN",
49
+ "ks": "KAS",
50
+ "mag": "MAG",
51
+ "mni": "MNI",
52
+ "sat": "SAT",
53
+ "brx": "BRX",
54
+ "guk": "GUK",
55
+ }
56
+ _BY_LOWER = {code.lower(): code for code in _LANGUAGES}
57
+ _FROM_TTS = {}
58
+ for _short, _tts in _LANGUAGES.items():
59
+ # ENG maps back to Indian English, the variant Oogam is built for.
60
+ _FROM_TTS.setdefault(_tts, "en-IN" if _tts == "ENG" else _short)
61
+
62
+ SUPPORTED_REALTIME_CODES = tuple(_LANGUAGES)
63
+
64
+
65
+ def _raw(language: Language | str) -> str:
66
+ value = language.value if isinstance(language, Language) else str(language)
67
+ return value.strip()
68
+
69
+
70
+ def to_realtime_code(language: Language | str | None) -> str | None:
71
+ """The realtime-socket code for a language, or None if Oogam does not serve it.
72
+
73
+ ``Language.HI`` and ``Language.HI_IN`` → ``hi``; ``Language.EN_IN`` →
74
+ ``en-IN``; any other English → ``en``; ``"GUJ"`` → ``gu``.
75
+ """
76
+ if language is None:
77
+ return None
78
+ raw = _raw(language)
79
+ if not raw:
80
+ return None
81
+ if raw.upper() in _FROM_TTS and len(raw) == 3 and raw.isupper():
82
+ return _FROM_TTS[raw.upper()]
83
+ low = raw.lower().replace("_", "-")
84
+ if low in _BY_LOWER:
85
+ return _BY_LOWER[low]
86
+ base = low.split("-")[0]
87
+ if base == "en":
88
+ return "en-IN" if low == "en-in" else "en"
89
+ return _BY_LOWER.get(base)
90
+
91
+
92
+ def to_tts_code(language: Language | str | None) -> str | None:
93
+ """The 3-letter speech code for a language, or None if Oogam does not serve it."""
94
+ short = to_realtime_code(language)
95
+ return _LANGUAGES.get(short) if short else None
96
+
97
+
98
+ def from_realtime_code(code: str | None) -> Language | None:
99
+ """A Pipecat ``Language`` for a realtime code, when the enum has one."""
100
+ if not code:
101
+ return None
102
+ try:
103
+ return Language(code)
104
+ except ValueError:
105
+ pass
106
+ try:
107
+ return Language(code.split("-")[0])
108
+ except ValueError:
109
+ return None