bodhi-api-sdk 1.0.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.
- bodhi_api_sdk-1.0.0/LICENSE +21 -0
- bodhi_api_sdk-1.0.0/PKG-INFO +265 -0
- bodhi_api_sdk-1.0.0/README.md +238 -0
- bodhi_api_sdk-1.0.0/bodhi/__init__.py +23 -0
- bodhi_api_sdk-1.0.0/bodhi/audio_processor.py +54 -0
- bodhi_api_sdk-1.0.0/bodhi/events.py +9 -0
- bodhi_api_sdk-1.0.0/bodhi/examples/__init__.py +7 -0
- bodhi_api_sdk-1.0.0/bodhi/examples/pipecat_mic_bot.py +107 -0
- bodhi_api_sdk-1.0.0/bodhi/examples/pipecat_stream_wav.py +143 -0
- bodhi_api_sdk-1.0.0/bodhi/integrations/__init__.py +9 -0
- bodhi_api_sdk-1.0.0/bodhi/integrations/pipecat_stt.py +639 -0
- bodhi_api_sdk-1.0.0/bodhi/transcription_client.py +161 -0
- bodhi_api_sdk-1.0.0/bodhi/transcription_config.py +64 -0
- bodhi_api_sdk-1.0.0/bodhi/transcription_handler.py +459 -0
- bodhi_api_sdk-1.0.0/bodhi/transcription_response.py +32 -0
- bodhi_api_sdk-1.0.0/bodhi/utils/error_utils.py +36 -0
- bodhi_api_sdk-1.0.0/bodhi/utils/exceptions.py +95 -0
- bodhi_api_sdk-1.0.0/bodhi/utils/logger.py +34 -0
- bodhi_api_sdk-1.0.0/bodhi/websocket_handler.py +292 -0
- bodhi_api_sdk-1.0.0/bodhi_api_sdk.egg-info/PKG-INFO +265 -0
- bodhi_api_sdk-1.0.0/bodhi_api_sdk.egg-info/SOURCES.txt +25 -0
- bodhi_api_sdk-1.0.0/bodhi_api_sdk.egg-info/dependency_links.txt +1 -0
- bodhi_api_sdk-1.0.0/bodhi_api_sdk.egg-info/requires.txt +5 -0
- bodhi_api_sdk-1.0.0/bodhi_api_sdk.egg-info/top_level.txt +1 -0
- bodhi_api_sdk-1.0.0/pyproject.toml +3 -0
- bodhi_api_sdk-1.0.0/setup.cfg +4 -0
- bodhi_api_sdk-1.0.0/setup.py +26 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Navana Tech India Private Limited
|
|
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,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bodhi-api-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Bodhi API Python SDK for Streaming Speech Recognition
|
|
5
|
+
Home-page: https://github.com/navana-tech/bodhi-python-sdk
|
|
6
|
+
Author: Navana
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: aiohttp
|
|
15
|
+
Provides-Extra: pipecat
|
|
16
|
+
Requires-Dist: pipecat-ai>=1.4; extra == "pipecat"
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: classifier
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: provides-extra
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
Dynamic: summary
|
|
27
|
+
|
|
28
|
+
# Bodhi API Python SDK
|
|
29
|
+
|
|
30
|
+
Bodhi API Python SDK provides a client for Navana's streaming speech recognition API.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install bodhi-api-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Moving from `bodhi-sdk`
|
|
39
|
+
|
|
40
|
+
`bodhi-api-sdk` is the SDK for Bodhi's new API platform. Your imports do not
|
|
41
|
+
change — only the package you install, and the credentials you pass:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip uninstall bodhi-sdk
|
|
45
|
+
pip install bodhi-api-sdk
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
# before, with bodhi-sdk
|
|
50
|
+
from bodhi import BodhiClient
|
|
51
|
+
client = BodhiClient(api_key=API_KEY, customer_id=CUSTOMER_ID)
|
|
52
|
+
|
|
53
|
+
# now, with bodhi-api-sdk
|
|
54
|
+
from bodhi import BodhiClient
|
|
55
|
+
client = BodhiClient(api_key=API_KEY)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Drop `customer_id` and you are done. Classes, methods, events, config fields and
|
|
59
|
+
`BODHI_API_KEY` are all unchanged. The default endpoint is now
|
|
60
|
+
`wss://stt.navana.ai`.
|
|
61
|
+
|
|
62
|
+
**Uninstall `bodhi-sdk` first.** Both packages install the same `bodhi` module,
|
|
63
|
+
so they cannot be installed at the same time — installing one on top of the
|
|
64
|
+
other leaves you with a mix of the two. `bodhi-sdk` 1.4.x keeps working against
|
|
65
|
+
the old endpoint and will be retired once everyone has moved across.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
To use the Bodhi Python SDK, follow these steps:
|
|
70
|
+
|
|
71
|
+
1. **Installation:**
|
|
72
|
+
Install the SDK using pip:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install bodhi-api-sdk
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Initialization:**
|
|
79
|
+
Create a `BodhiClient` instance with your API key:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from bodhi import BodhiClient
|
|
83
|
+
|
|
84
|
+
client = BodhiClient(api_key="YOUR_API_KEY")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
3. **Transcription:**
|
|
88
|
+
Use the client methods to transcribe audio. The SDK supports transcription from local files, remote URLs, and streams.
|
|
89
|
+
|
|
90
|
+
- **Local File Transcription:**
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
config = TranscriptionConfig(
|
|
94
|
+
model="hi-banking-v2-8khz",
|
|
95
|
+
at_start_lid=False, # Enable language identification at start (default: False)
|
|
96
|
+
transliterate=False, # Enable transliteration output (default: False)
|
|
97
|
+
endpoint_silence_duration=0.6, # Trailing silence before an utterance
|
|
98
|
+
# is finalised, in seconds. Omitted
|
|
99
|
+
# unless set; server default 0.44,
|
|
100
|
+
# clamped to 0.44-1.2
|
|
101
|
+
)
|
|
102
|
+
response = client.transcribe_local_file(audio_file_path, config=config)
|
|
103
|
+
print(response.text)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **Remote URL Transcription:**
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
config = TranscriptionConfig(
|
|
110
|
+
model="hi-banking-v2-8khz",
|
|
111
|
+
at_start_lid=False, # Enable language identification at start (default: False)
|
|
112
|
+
transliterate=False, # Enable transliteration output (default: False)
|
|
113
|
+
endpoint_silence_duration=0.6, # Trailing silence before an utterance
|
|
114
|
+
# is finalised, in seconds. Omitted
|
|
115
|
+
# unless set; server default 0.44,
|
|
116
|
+
# clamped to 0.44-1.2
|
|
117
|
+
)
|
|
118
|
+
response = client.transcribe_remote_url("http://example.com/audio.wav", config)
|
|
119
|
+
print(response.text)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **Streaming Transcription:**
|
|
123
|
+
Refer to the examples for detailed instructions on setting up streaming transcription.
|
|
124
|
+
|
|
125
|
+
4. **Event Handling:**
|
|
126
|
+
You can register event listeners to handle different stages of the transcription process using the `client.on` method and the `LiveTranscriptionEvents` enum. This is particularly useful for streaming and remote URL transcriptions where events are emitted asynchronously.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from bodhi import LiveTranscriptionEvents
|
|
130
|
+
|
|
131
|
+
async def on_transcript(response):
|
|
132
|
+
print(f"Transcript: {response.text}")
|
|
133
|
+
|
|
134
|
+
async def on_utterance_end(response):
|
|
135
|
+
print(f"UtteranceEnd: {response}")
|
|
136
|
+
|
|
137
|
+
async def on_speech_started(response):
|
|
138
|
+
print(f"SpeechStarted: {response}")
|
|
139
|
+
|
|
140
|
+
async def on_error(e):
|
|
141
|
+
print(f"Error: {str(e)}")
|
|
142
|
+
|
|
143
|
+
client.on(LiveTranscriptionEvents.Transcript, on_transcript)
|
|
144
|
+
client.on(LiveTranscriptionEvents.UtteranceEnd, on_utterance_end)
|
|
145
|
+
client.on(LiveTranscriptionEvents.SpeechStarted, on_speech_started)
|
|
146
|
+
client.on(LiveTranscriptionEvents.Error, on_error)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Common events include:
|
|
150
|
+
|
|
151
|
+
- `LiveTranscriptionEvents.Transcript`: Emitted when a new transcription segment is available.
|
|
152
|
+
- `LiveTranscriptionEvents.UtteranceEnd`: Emitted when an utterance is detected as complete.
|
|
153
|
+
- `LiveTranscriptionEvents.SpeechStarted`: Emitted when speech activity is detected.
|
|
154
|
+
- `LiveTranscriptionEvents.Error`: Emitted when an error occurs during transcription.
|
|
155
|
+
- `LiveTranscriptionEvents.Close`: Emitted when the WebSocket connection is closed.
|
|
156
|
+
|
|
157
|
+
## Pipecat integration
|
|
158
|
+
|
|
159
|
+
Building a voice agent with [Pipecat](https://github.com/pipecat-ai/pipecat)? Bodhi
|
|
160
|
+
drops into the STT slot of a Pipecat pipeline:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install "bodhi-api-sdk[pipecat]"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import os
|
|
168
|
+
|
|
169
|
+
from pipecat.pipeline.pipeline import Pipeline
|
|
170
|
+
|
|
171
|
+
from bodhi.integrations.pipecat_stt import BodhiHotword, BodhiSTTService
|
|
172
|
+
|
|
173
|
+
stt = BodhiSTTService(
|
|
174
|
+
api_key=os.environ["BODHI_API_KEY"],
|
|
175
|
+
model="hi-general-v2-8khz",
|
|
176
|
+
settings=BodhiSTTService.Settings(
|
|
177
|
+
parse_number=True, # normalise numbers, dates, currency
|
|
178
|
+
endpoint_silence_duration=0.6, # server-side endpointing, 0.44-1.2s
|
|
179
|
+
hotwords=[BodhiHotword("बजाज फिनसर्व", 2.0)],
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
pipeline = Pipeline([
|
|
184
|
+
transport.input(),
|
|
185
|
+
stt,
|
|
186
|
+
context_aggregator.user(),
|
|
187
|
+
llm,
|
|
188
|
+
tts,
|
|
189
|
+
transport.output(),
|
|
190
|
+
])
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
That is the whole integration — `api_key` and `model` are the only
|
|
194
|
+
required arguments, and `url` defaults to `wss://stt.navana.ai`. Bodhi's
|
|
195
|
+
partial results arrive as `InterimTranscriptionFrame`s and its endpointed final
|
|
196
|
+
results as `TranscriptionFrame`s, so interruption handling and turn taking work
|
|
197
|
+
exactly as they do with any other Pipecat STT service.
|
|
198
|
+
|
|
199
|
+
Maintained by [Navana Tech](https://navana.ai/), who build Bodhi.
|
|
200
|
+
|
|
201
|
+
Written for pipecat-ai 1.4 and verified on 1.4.0 and 1.8.1. Streaming and
|
|
202
|
+
transcripts also work as far back as 1.0.0; switching model or hotwords at
|
|
203
|
+
runtime needs 1.4+, since that is where Pipecat's reconnect hook arrived. Needs
|
|
204
|
+
Python 3.10+, as Pipecat does.
|
|
205
|
+
|
|
206
|
+
If you pin `pipecat-ai` below 1.4, install plain `bodhi-api-sdk` (so pip doesn't
|
|
207
|
+
touch your pin) and import the same module, or copy
|
|
208
|
+
`bodhi/integrations/pipecat_stt.py` into your project — it is self-contained and
|
|
209
|
+
imports nothing else from this SDK.
|
|
210
|
+
|
|
211
|
+
### Advanced features
|
|
212
|
+
|
|
213
|
+
Every field from the streaming
|
|
214
|
+
[advanced features](https://docs.navana.ai/speech-to-text/advanced-features/)
|
|
215
|
+
page is reachable from here:
|
|
216
|
+
|
|
217
|
+
| Bodhi feature | How to set it |
|
|
218
|
+
|---|---|
|
|
219
|
+
| Context biasing (hotwords) | `Settings(hotwords=[BodhiHotword("phrase", 2.0)])` |
|
|
220
|
+
| Endpoint silence threshold | `Settings(endpoint_silence_duration=0.6)` — seconds; server default 0.44, clamped to 0.44–1.2 |
|
|
221
|
+
| Parse numbers into numerals | `Settings(parse_number=True)` |
|
|
222
|
+
| Partial result exclusion | `BodhiSTTService(..., interim_results=False)` — also stops the server sending them |
|
|
223
|
+
| Aux metadata | `BodhiSTTService(..., aux=True)` |
|
|
224
|
+
| Confidence and word timings | already on every frame's `result` |
|
|
225
|
+
| Confidence filtering | `BodhiSTTService(..., min_confidence=0.5)` — Pipecat's guidance; `0` keeps everything |
|
|
226
|
+
|
|
227
|
+
`result` carries the raw Bodhi message, so word timings and confidence are
|
|
228
|
+
there on every final without setting any flag:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
segment = frame.result["segment_meta"]
|
|
232
|
+
segment["confidence"] # 0.87 — utterance level
|
|
233
|
+
segment["words"][0] # {"word": "आपने", "confidence": 0.873,
|
|
234
|
+
# "start_time": 0.32, "end_time": 0.48}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`aux=True` adds an `aux_info` block on top of that, with `request_time`,
|
|
238
|
+
`eot_wait_time` and `processed_audio_duration` for latency debugging.
|
|
239
|
+
|
|
240
|
+
### Trying it out
|
|
241
|
+
|
|
242
|
+
Two runnable examples, neither needing an LLM or TTS key:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
export BODHI_API_KEY=...
|
|
246
|
+
|
|
247
|
+
# 1. Transcribe a recording through a real Pipecat pipeline.
|
|
248
|
+
curl -O https://stt.navana.ai/audios/loan.wav
|
|
249
|
+
python -m bodhi.examples.pipecat_stream_wav loan.wav --model hi-banking-v2-8khz
|
|
250
|
+
|
|
251
|
+
# 2. Transcribe your microphone live in the browser.
|
|
252
|
+
pip install "pipecat-ai[webrtc,silero,runner]"
|
|
253
|
+
python -m bodhi.examples.pipecat_mic_bot # then open http://localhost:7860/client
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
For a full talking bot, follow the
|
|
257
|
+
[Pipecat quickstart](https://docs.pipecat.ai/pipecat/get-started/quickstart) and
|
|
258
|
+
replace its `DeepgramSTTService(...)` line with the `BodhiSTTService(...)` above.
|
|
259
|
+
|
|
260
|
+
`docs/pipecat-listing/bodhi.mdx` is this integration's service page for
|
|
261
|
+
docs.pipecat.ai, submitted as a
|
|
262
|
+
[community integration](https://github.com/pipecat-ai/pipecat/blob/main/COMMUNITY_INTEGRATIONS.md).
|
|
263
|
+
Keep it in step with the parameters above.
|
|
264
|
+
|
|
265
|
+
For complete code examples and detailed usage instructions for various scenarios, please refer to the [official documentation](https://docs.navana.ai/introduction/).
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Bodhi API Python SDK
|
|
2
|
+
|
|
3
|
+
Bodhi API Python SDK provides a client for Navana's streaming speech recognition API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install bodhi-api-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Moving from `bodhi-sdk`
|
|
12
|
+
|
|
13
|
+
`bodhi-api-sdk` is the SDK for Bodhi's new API platform. Your imports do not
|
|
14
|
+
change — only the package you install, and the credentials you pass:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip uninstall bodhi-sdk
|
|
18
|
+
pip install bodhi-api-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
# before, with bodhi-sdk
|
|
23
|
+
from bodhi import BodhiClient
|
|
24
|
+
client = BodhiClient(api_key=API_KEY, customer_id=CUSTOMER_ID)
|
|
25
|
+
|
|
26
|
+
# now, with bodhi-api-sdk
|
|
27
|
+
from bodhi import BodhiClient
|
|
28
|
+
client = BodhiClient(api_key=API_KEY)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Drop `customer_id` and you are done. Classes, methods, events, config fields and
|
|
32
|
+
`BODHI_API_KEY` are all unchanged. The default endpoint is now
|
|
33
|
+
`wss://stt.navana.ai`.
|
|
34
|
+
|
|
35
|
+
**Uninstall `bodhi-sdk` first.** Both packages install the same `bodhi` module,
|
|
36
|
+
so they cannot be installed at the same time — installing one on top of the
|
|
37
|
+
other leaves you with a mix of the two. `bodhi-sdk` 1.4.x keeps working against
|
|
38
|
+
the old endpoint and will be retired once everyone has moved across.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
To use the Bodhi Python SDK, follow these steps:
|
|
43
|
+
|
|
44
|
+
1. **Installation:**
|
|
45
|
+
Install the SDK using pip:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install bodhi-api-sdk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. **Initialization:**
|
|
52
|
+
Create a `BodhiClient` instance with your API key:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from bodhi import BodhiClient
|
|
56
|
+
|
|
57
|
+
client = BodhiClient(api_key="YOUR_API_KEY")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
3. **Transcription:**
|
|
61
|
+
Use the client methods to transcribe audio. The SDK supports transcription from local files, remote URLs, and streams.
|
|
62
|
+
|
|
63
|
+
- **Local File Transcription:**
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
config = TranscriptionConfig(
|
|
67
|
+
model="hi-banking-v2-8khz",
|
|
68
|
+
at_start_lid=False, # Enable language identification at start (default: False)
|
|
69
|
+
transliterate=False, # Enable transliteration output (default: False)
|
|
70
|
+
endpoint_silence_duration=0.6, # Trailing silence before an utterance
|
|
71
|
+
# is finalised, in seconds. Omitted
|
|
72
|
+
# unless set; server default 0.44,
|
|
73
|
+
# clamped to 0.44-1.2
|
|
74
|
+
)
|
|
75
|
+
response = client.transcribe_local_file(audio_file_path, config=config)
|
|
76
|
+
print(response.text)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- **Remote URL Transcription:**
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
config = TranscriptionConfig(
|
|
83
|
+
model="hi-banking-v2-8khz",
|
|
84
|
+
at_start_lid=False, # Enable language identification at start (default: False)
|
|
85
|
+
transliterate=False, # Enable transliteration output (default: False)
|
|
86
|
+
endpoint_silence_duration=0.6, # Trailing silence before an utterance
|
|
87
|
+
# is finalised, in seconds. Omitted
|
|
88
|
+
# unless set; server default 0.44,
|
|
89
|
+
# clamped to 0.44-1.2
|
|
90
|
+
)
|
|
91
|
+
response = client.transcribe_remote_url("http://example.com/audio.wav", config)
|
|
92
|
+
print(response.text)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **Streaming Transcription:**
|
|
96
|
+
Refer to the examples for detailed instructions on setting up streaming transcription.
|
|
97
|
+
|
|
98
|
+
4. **Event Handling:**
|
|
99
|
+
You can register event listeners to handle different stages of the transcription process using the `client.on` method and the `LiveTranscriptionEvents` enum. This is particularly useful for streaming and remote URL transcriptions where events are emitted asynchronously.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from bodhi import LiveTranscriptionEvents
|
|
103
|
+
|
|
104
|
+
async def on_transcript(response):
|
|
105
|
+
print(f"Transcript: {response.text}")
|
|
106
|
+
|
|
107
|
+
async def on_utterance_end(response):
|
|
108
|
+
print(f"UtteranceEnd: {response}")
|
|
109
|
+
|
|
110
|
+
async def on_speech_started(response):
|
|
111
|
+
print(f"SpeechStarted: {response}")
|
|
112
|
+
|
|
113
|
+
async def on_error(e):
|
|
114
|
+
print(f"Error: {str(e)}")
|
|
115
|
+
|
|
116
|
+
client.on(LiveTranscriptionEvents.Transcript, on_transcript)
|
|
117
|
+
client.on(LiveTranscriptionEvents.UtteranceEnd, on_utterance_end)
|
|
118
|
+
client.on(LiveTranscriptionEvents.SpeechStarted, on_speech_started)
|
|
119
|
+
client.on(LiveTranscriptionEvents.Error, on_error)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Common events include:
|
|
123
|
+
|
|
124
|
+
- `LiveTranscriptionEvents.Transcript`: Emitted when a new transcription segment is available.
|
|
125
|
+
- `LiveTranscriptionEvents.UtteranceEnd`: Emitted when an utterance is detected as complete.
|
|
126
|
+
- `LiveTranscriptionEvents.SpeechStarted`: Emitted when speech activity is detected.
|
|
127
|
+
- `LiveTranscriptionEvents.Error`: Emitted when an error occurs during transcription.
|
|
128
|
+
- `LiveTranscriptionEvents.Close`: Emitted when the WebSocket connection is closed.
|
|
129
|
+
|
|
130
|
+
## Pipecat integration
|
|
131
|
+
|
|
132
|
+
Building a voice agent with [Pipecat](https://github.com/pipecat-ai/pipecat)? Bodhi
|
|
133
|
+
drops into the STT slot of a Pipecat pipeline:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pip install "bodhi-api-sdk[pipecat]"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
import os
|
|
141
|
+
|
|
142
|
+
from pipecat.pipeline.pipeline import Pipeline
|
|
143
|
+
|
|
144
|
+
from bodhi.integrations.pipecat_stt import BodhiHotword, BodhiSTTService
|
|
145
|
+
|
|
146
|
+
stt = BodhiSTTService(
|
|
147
|
+
api_key=os.environ["BODHI_API_KEY"],
|
|
148
|
+
model="hi-general-v2-8khz",
|
|
149
|
+
settings=BodhiSTTService.Settings(
|
|
150
|
+
parse_number=True, # normalise numbers, dates, currency
|
|
151
|
+
endpoint_silence_duration=0.6, # server-side endpointing, 0.44-1.2s
|
|
152
|
+
hotwords=[BodhiHotword("बजाज फिनसर्व", 2.0)],
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
pipeline = Pipeline([
|
|
157
|
+
transport.input(),
|
|
158
|
+
stt,
|
|
159
|
+
context_aggregator.user(),
|
|
160
|
+
llm,
|
|
161
|
+
tts,
|
|
162
|
+
transport.output(),
|
|
163
|
+
])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
That is the whole integration — `api_key` and `model` are the only
|
|
167
|
+
required arguments, and `url` defaults to `wss://stt.navana.ai`. Bodhi's
|
|
168
|
+
partial results arrive as `InterimTranscriptionFrame`s and its endpointed final
|
|
169
|
+
results as `TranscriptionFrame`s, so interruption handling and turn taking work
|
|
170
|
+
exactly as they do with any other Pipecat STT service.
|
|
171
|
+
|
|
172
|
+
Maintained by [Navana Tech](https://navana.ai/), who build Bodhi.
|
|
173
|
+
|
|
174
|
+
Written for pipecat-ai 1.4 and verified on 1.4.0 and 1.8.1. Streaming and
|
|
175
|
+
transcripts also work as far back as 1.0.0; switching model or hotwords at
|
|
176
|
+
runtime needs 1.4+, since that is where Pipecat's reconnect hook arrived. Needs
|
|
177
|
+
Python 3.10+, as Pipecat does.
|
|
178
|
+
|
|
179
|
+
If you pin `pipecat-ai` below 1.4, install plain `bodhi-api-sdk` (so pip doesn't
|
|
180
|
+
touch your pin) and import the same module, or copy
|
|
181
|
+
`bodhi/integrations/pipecat_stt.py` into your project — it is self-contained and
|
|
182
|
+
imports nothing else from this SDK.
|
|
183
|
+
|
|
184
|
+
### Advanced features
|
|
185
|
+
|
|
186
|
+
Every field from the streaming
|
|
187
|
+
[advanced features](https://docs.navana.ai/speech-to-text/advanced-features/)
|
|
188
|
+
page is reachable from here:
|
|
189
|
+
|
|
190
|
+
| Bodhi feature | How to set it |
|
|
191
|
+
|---|---|
|
|
192
|
+
| Context biasing (hotwords) | `Settings(hotwords=[BodhiHotword("phrase", 2.0)])` |
|
|
193
|
+
| Endpoint silence threshold | `Settings(endpoint_silence_duration=0.6)` — seconds; server default 0.44, clamped to 0.44–1.2 |
|
|
194
|
+
| Parse numbers into numerals | `Settings(parse_number=True)` |
|
|
195
|
+
| Partial result exclusion | `BodhiSTTService(..., interim_results=False)` — also stops the server sending them |
|
|
196
|
+
| Aux metadata | `BodhiSTTService(..., aux=True)` |
|
|
197
|
+
| Confidence and word timings | already on every frame's `result` |
|
|
198
|
+
| Confidence filtering | `BodhiSTTService(..., min_confidence=0.5)` — Pipecat's guidance; `0` keeps everything |
|
|
199
|
+
|
|
200
|
+
`result` carries the raw Bodhi message, so word timings and confidence are
|
|
201
|
+
there on every final without setting any flag:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
segment = frame.result["segment_meta"]
|
|
205
|
+
segment["confidence"] # 0.87 — utterance level
|
|
206
|
+
segment["words"][0] # {"word": "आपने", "confidence": 0.873,
|
|
207
|
+
# "start_time": 0.32, "end_time": 0.48}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`aux=True` adds an `aux_info` block on top of that, with `request_time`,
|
|
211
|
+
`eot_wait_time` and `processed_audio_duration` for latency debugging.
|
|
212
|
+
|
|
213
|
+
### Trying it out
|
|
214
|
+
|
|
215
|
+
Two runnable examples, neither needing an LLM or TTS key:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
export BODHI_API_KEY=...
|
|
219
|
+
|
|
220
|
+
# 1. Transcribe a recording through a real Pipecat pipeline.
|
|
221
|
+
curl -O https://stt.navana.ai/audios/loan.wav
|
|
222
|
+
python -m bodhi.examples.pipecat_stream_wav loan.wav --model hi-banking-v2-8khz
|
|
223
|
+
|
|
224
|
+
# 2. Transcribe your microphone live in the browser.
|
|
225
|
+
pip install "pipecat-ai[webrtc,silero,runner]"
|
|
226
|
+
python -m bodhi.examples.pipecat_mic_bot # then open http://localhost:7860/client
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For a full talking bot, follow the
|
|
230
|
+
[Pipecat quickstart](https://docs.pipecat.ai/pipecat/get-started/quickstart) and
|
|
231
|
+
replace its `DeepgramSTTService(...)` line with the `BodhiSTTService(...)` above.
|
|
232
|
+
|
|
233
|
+
`docs/pipecat-listing/bodhi.mdx` is this integration's service page for
|
|
234
|
+
docs.pipecat.ai, submitted as a
|
|
235
|
+
[community integration](https://github.com/pipecat-ai/pipecat/blob/main/COMMUNITY_INTEGRATIONS.md).
|
|
236
|
+
Keep it in step with the parameters above.
|
|
237
|
+
|
|
238
|
+
For complete code examples and detailed usage instructions for various scenarios, please refer to the [official documentation](https://docs.navana.ai/introduction/).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bodhi Python SDK - Streaming Speech Recognition
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "1.0.0"
|
|
6
|
+
|
|
7
|
+
EOF_SIGNAL = '{"eof": 1}'
|
|
8
|
+
|
|
9
|
+
from .transcription_client import BodhiClient
|
|
10
|
+
from .transcription_config import TranscriptionConfig, Hotword
|
|
11
|
+
from .transcription_response import TranscriptionResponse
|
|
12
|
+
from .events import LiveTranscriptionEvents
|
|
13
|
+
from bodhi.utils.error_utils import BodhiErrors
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BodhiClient",
|
|
17
|
+
"TranscriptionConfig",
|
|
18
|
+
"TranscriptionResponse",
|
|
19
|
+
"Hotword",
|
|
20
|
+
"LiveTranscriptionEvents",
|
|
21
|
+
"EOF_SIGNAL",
|
|
22
|
+
BodhiErrors,
|
|
23
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Audio Processing Module for Bodhi Client"""
|
|
2
|
+
|
|
3
|
+
import wave
|
|
4
|
+
import asyncio
|
|
5
|
+
from typing import BinaryIO, Any
|
|
6
|
+
from .utils.logger import logger
|
|
7
|
+
from . import EOF_SIGNAL
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AudioProcessor:
|
|
11
|
+
@staticmethod
|
|
12
|
+
async def process_file(
|
|
13
|
+
ws: Any,
|
|
14
|
+
wf: wave.Wave_read,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Process and stream audio file data.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
ws: WebSocket connection
|
|
20
|
+
wf: Wave file object
|
|
21
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
|
|
24
|
+
REALTIME_RESOLUTION = 0.02 # 20ms
|
|
25
|
+
byte_rate = wf.getframerate() * wf.getsampwidth() * wf.getnchannels()
|
|
26
|
+
data = wf.readframes(wf.getnframes())
|
|
27
|
+
audio_cursor = 0
|
|
28
|
+
|
|
29
|
+
while len(data):
|
|
30
|
+
i = int(byte_rate * REALTIME_RESOLUTION)
|
|
31
|
+
chunk, data = data[:i], data[i:]
|
|
32
|
+
if not ws.closed:
|
|
33
|
+
await ws.send_bytes(chunk)
|
|
34
|
+
logger.debug(f"Sent {len(chunk)} bytes of audio data")
|
|
35
|
+
audio_cursor += REALTIME_RESOLUTION
|
|
36
|
+
await asyncio.sleep(REALTIME_RESOLUTION)
|
|
37
|
+
|
|
38
|
+
if not ws.closed:
|
|
39
|
+
await ws.send_str(EOF_SIGNAL)
|
|
40
|
+
logger.debug("Sent EOF signal")
|
|
41
|
+
except Exception as e:
|
|
42
|
+
return e
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
async def process_stream(stream: BinaryIO, ws: Any) -> None:
|
|
46
|
+
"""Process and stream generic binary stream data."""
|
|
47
|
+
try:
|
|
48
|
+
data = stream.read()
|
|
49
|
+
if not ws.closed:
|
|
50
|
+
await ws.send_bytes(data)
|
|
51
|
+
logger.debug(f"Sent {len(data)} bytes of stream data")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.exception("Error during stream processing")
|
|
54
|
+
raise e
|