webscout 8.3.5__py3-none-any.whl → 8.3.6__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.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (63) hide show
  1. webscout/Bard.py +12 -6
  2. webscout/DWEBS.py +66 -57
  3. webscout/Provider/{UNFINISHED → AISEARCH}/PERPLEXED_search.py +34 -74
  4. webscout/Provider/AISEARCH/__init__.py +1 -1
  5. webscout/Provider/Deepinfra.py +6 -0
  6. webscout/Provider/Flowith.py +6 -1
  7. webscout/Provider/GithubChat.py +1 -0
  8. webscout/Provider/GptOss.py +207 -0
  9. webscout/Provider/Kimi.py +445 -0
  10. webscout/Provider/Netwrck.py +3 -6
  11. webscout/Provider/OPENAI/README.md +2 -1
  12. webscout/Provider/OPENAI/TogetherAI.py +50 -55
  13. webscout/Provider/OPENAI/__init__.py +4 -2
  14. webscout/Provider/OPENAI/copilot.py +20 -4
  15. webscout/Provider/OPENAI/deepinfra.py +6 -0
  16. webscout/Provider/OPENAI/e2b.py +60 -8
  17. webscout/Provider/OPENAI/flowith.py +4 -3
  18. webscout/Provider/OPENAI/generate_api_key.py +48 -0
  19. webscout/Provider/OPENAI/gptoss.py +288 -0
  20. webscout/Provider/OPENAI/kimi.py +469 -0
  21. webscout/Provider/OPENAI/netwrck.py +8 -12
  22. webscout/Provider/OPENAI/refact.py +274 -0
  23. webscout/Provider/OPENAI/textpollinations.py +3 -6
  24. webscout/Provider/OPENAI/toolbaz.py +1 -0
  25. webscout/Provider/TTI/bing.py +14 -2
  26. webscout/Provider/TTI/together.py +10 -9
  27. webscout/Provider/TTS/README.md +0 -1
  28. webscout/Provider/TTS/__init__.py +0 -1
  29. webscout/Provider/TTS/base.py +479 -159
  30. webscout/Provider/TTS/deepgram.py +409 -156
  31. webscout/Provider/TTS/elevenlabs.py +425 -111
  32. webscout/Provider/TTS/freetts.py +317 -140
  33. webscout/Provider/TTS/gesserit.py +192 -128
  34. webscout/Provider/TTS/murfai.py +248 -113
  35. webscout/Provider/TTS/openai_fm.py +347 -129
  36. webscout/Provider/TTS/speechma.py +620 -586
  37. webscout/Provider/TextPollinationsAI.py +3 -6
  38. webscout/Provider/TogetherAI.py +50 -55
  39. webscout/Provider/UNFINISHED/VercelAIGateway.py +339 -0
  40. webscout/Provider/__init__.py +2 -90
  41. webscout/Provider/cerebras.py +83 -33
  42. webscout/Provider/copilot.py +42 -23
  43. webscout/Provider/toolbaz.py +1 -0
  44. webscout/conversation.py +22 -20
  45. webscout/sanitize.py +14 -10
  46. webscout/scout/README.md +20 -23
  47. webscout/scout/core/crawler.py +125 -38
  48. webscout/scout/core/scout.py +26 -5
  49. webscout/version.py +1 -1
  50. webscout/webscout_search.py +13 -6
  51. webscout/webscout_search_async.py +10 -8
  52. webscout/yep_search.py +13 -5
  53. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/METADATA +2 -1
  54. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/RECORD +59 -56
  55. webscout/Provider/Glider.py +0 -225
  56. webscout/Provider/OPENAI/c4ai.py +0 -394
  57. webscout/Provider/OPENAI/glider.py +0 -330
  58. webscout/Provider/TTS/sthir.py +0 -94
  59. /webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +0 -0
  60. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/WHEEL +0 -0
  61. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/entry_points.txt +0 -0
  62. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/licenses/LICENSE.md +0 -0
  63. {webscout-8.3.5.dist-info → webscout-8.3.6.dist-info}/top_level.txt +0 -0
@@ -1,111 +1,425 @@
1
- import time
2
- import requests
3
- import pathlib
4
- import tempfile
5
- from io import BytesIO
6
- from webscout import exceptions
7
- from webscout.litagent import LitAgent
8
- from concurrent.futures import ThreadPoolExecutor, as_completed
9
- from . import utils
10
- from .base import BaseTTSProvider
11
-
12
- class ElevenlabsTTS(BaseTTSProvider):
13
- """
14
- Text-to-speech provider using the ElevenlabsTTS API.
15
- """
16
- # Request headers
17
- headers: dict[str, str] = {
18
- "User-Agent": LitAgent().random()
19
- }
20
- all_voices: dict[str, str] = {"Brian": "nPczCjzI2devNBz1zQrb", "Alice":"Xb7hH8MSUJpSbSDYk0k2", "Bill":"pqHfZKP75CvOlQylNhV4", "Callum":"N2lVS1w4EtoT3dr4eOWO", "Charlie":"IKne3meq5aSn9XLyUdCD", "Charlotte":"XB0fDUnXU5powFXDhCwa", "Chris":"iP95p4xoKVk53GoZ742B", "Daniel":"onwK4e9ZLuTAKqWW03F9", "Eric":"cjVigY5qzO86Huf0OWal", "George":"JBFqnCBsd6RMkjVDRZzb", "Jessica":"cgSgspJ2msm6clMCkdW9", "Laura":"FGY2WhTYpPnrIDTdsKH5", "Liam":"TX3LPaxmHKxFdv7VOQHJ", "Lily":"pFZP5JQG7iQjIQuC4Bku", "Matilda":"XrExE9yKIg1WjnnlVkGX", "Sarah":"EXAVITQu4vr4xnSDxMaL", "Will":"bIHbv24MWmeRgasZH58o"}
21
-
22
- def __init__(self, timeout: int = 20, proxies: dict = None):
23
- """Initializes the ElevenlabsTTS TTS client."""
24
- super().__init__()
25
- self.session = requests.Session()
26
- self.session.headers.update(self.headers)
27
- if proxies:
28
- self.session.proxies.update(proxies)
29
- self.timeout = timeout
30
- self.params = {'allow_unauthenticated': '1'}
31
-
32
- def tts(self, text: str, voice: str = "Brian", verbose:bool = True) -> str:
33
- """
34
- Converts text to speech using the ElevenlabsTTS API and saves it to a file.
35
- """
36
- assert (
37
- voice in self.all_voices
38
- ), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
39
-
40
- filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
41
-
42
- # Split text into sentences
43
- sentences = utils.split_sentences(text)
44
-
45
- # Function to request audio for each chunk
46
- def generate_audio_for_chunk(part_text: str, part_number: int):
47
- while True:
48
- try:
49
- json_data = {'text': part_text, 'model_id': 'eleven_multilingual_v2'}
50
- response = self.session.post(f'https://api.elevenlabs.io/v1/text-to-speech/{self.all_voices[voice]}',params=self.params, headers=self.headers, json=json_data, timeout=self.timeout)
51
- response.raise_for_status()
52
-
53
- # Check if the request was successful
54
- if response.ok and response.status_code == 200:
55
- if verbose:
56
- print(f"[debug] Chunk {part_number} processed successfully")
57
- return part_number, response.content
58
- else:
59
- if verbose:
60
- print(f"[debug] No data received for chunk {part_number}. Retrying...")
61
- except requests.RequestException as e:
62
- if verbose:
63
- print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
64
- time.sleep(1)
65
- try:
66
- # Using ThreadPoolExecutor to handle requests concurrently
67
- with ThreadPoolExecutor() as executor:
68
- futures = {executor.submit(generate_audio_for_chunk, sentence.strip(), chunk_num): chunk_num
69
- for chunk_num, sentence in enumerate(sentences, start=1)}
70
-
71
- # Dictionary to store results with order preserved
72
- audio_chunks = {}
73
-
74
- for future in as_completed(futures):
75
- chunk_num = futures[future]
76
- try:
77
- part_number, audio_data = future.result()
78
- audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
79
- except Exception as e:
80
- if verbose:
81
- print(f"[debug] Failed to generate audio for chunk {chunk_num}: {e}")
82
-
83
- # Combine audio chunks in the correct sequence
84
- combined_audio = BytesIO()
85
- for part_number in sorted(audio_chunks.keys()):
86
- combined_audio.write(audio_chunks[part_number])
87
- if verbose:
88
- print(f"[debug] Added chunk {part_number} to the combined file.")
89
-
90
- # Save the combined audio data to a single file
91
- with open(filename, 'wb') as f:
92
- f.write(combined_audio.getvalue())
93
- if verbose:
94
- print(f"[debug] Final Audio Saved as {filename}")
95
- return filename.as_posix()
96
-
97
- except requests.exceptions.RequestException as e:
98
- if verbose:
99
- print(f"[debug] Failed to perform the operation: {e}")
100
- raise exceptions.FailedToGenerateResponseError(
101
- f"Failed to perform the operation: {e}"
102
- )
103
-
104
- # Example usage
105
- if __name__ == "__main__":
106
- elevenlabs = ElevenlabsTTS()
107
- text = "This is a test of the ElevenlabsTTS text-to-speech API. It supports multiple sentences and advanced logging."
108
-
109
- print("[debug] Generating audio...")
110
- audio_file = elevenlabs.tts(text, voice="Brian")
111
- print(f"Audio saved to: {audio_file}")
1
+ ##################################################################################
2
+ ## ElevenLabs TTS Provider ##
3
+ ##################################################################################
4
+ import time
5
+ import requests
6
+ import pathlib
7
+ import tempfile
8
+ from io import BytesIO
9
+ from webscout import exceptions
10
+ from webscout.litagent import LitAgent
11
+ from concurrent.futures import ThreadPoolExecutor, as_completed
12
+
13
+ try:
14
+ from . import utils
15
+ from .base import BaseTTSProvider
16
+ except ImportError:
17
+ # Handle direct execution
18
+ import sys
19
+ import os
20
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
21
+ from webscout.Provider.TTS import utils
22
+ from webscout.Provider.TTS.base import BaseTTSProvider
23
+
24
+ class ElevenlabsTTS(BaseTTSProvider):
25
+ """
26
+ Text-to-speech provider using the ElevenLabs API with OpenAI-compatible interface.
27
+
28
+ This provider follows the OpenAI TTS API structure with support for:
29
+ - Multiple TTS models (gpt-4o-mini-tts, tts-1, tts-1-hd)
30
+ - ElevenLabs' multilingual voices
31
+ - Voice instructions for controlling speech aspects
32
+ - Multiple output formats
33
+ - Streaming support
34
+ """
35
+
36
+ # Request headers
37
+ headers: dict[str, str] = {
38
+ "User-Agent": LitAgent().random()
39
+ }
40
+
41
+ # Override supported models for ElevenLabs
42
+ SUPPORTED_MODELS = None
43
+
44
+ # ElevenLabs voices (mapped to OpenAI-compatible names)
45
+ SUPPORTED_VOICES = [
46
+ "brian", # nPczCjzI2devNBz1zQrb - Deep male voice
47
+ "alice", # Xb7hH8MSUJpSbSDYk0k2 - Natural female voice
48
+ "bill", # pqHfZKP75CvOlQylNhV4 - Warm male voice
49
+ "callum", # N2lVS1w4EtoT3dr4eOWO - Scottish male voice
50
+ "charlie", # IKne3meq5aSn9XLyUdCD - American male voice
51
+ "charlotte", # XB0fDUnXU5powFXDhCwa - British female voice
52
+ "chris", # iP95p4xoKVk53GoZ742B - American male voice
53
+ "daniel", # onwK4e9ZLuTAKqWW03F9 - British male voice
54
+ "eric", # cjVigY5qzO86Huf0OWal - American male voice
55
+ "george", # JBFqnCBsd6RMkjVDRZzb - Raspy male voice
56
+ "jessica", # cgSgspJ2msm6clMCkdW9 - Warm female voice
57
+ "laura", # FGY2WhTYpPnrIDTdsKH5 - Soft female voice
58
+ "liam", # TX3LPaxmHKxFdv7VOQHJ - Irish male voice
59
+ "lily", # pFZP5JQG7iQjIQuC4Bku - British female voice
60
+ "matilda", # XrExE9yKIg1WjnnlVkGX - Warm female voice
61
+ "sarah", # EXAVITQu4vr4xnSDxMaL - Soft female voice
62
+ "will" # bIHbv24MWmeRgasZH58o - American male voice
63
+ ]
64
+
65
+ # Voice mapping for ElevenLabs API compatibility
66
+ voice_mapping = {
67
+ "brian": "nPczCjzI2devNBz1zQrb",
68
+ "alice": "Xb7hH8MSUJpSbSDYk0k2",
69
+ "bill": "pqHfZKP75CvOlQylNhV4",
70
+ "callum": "N2lVS1w4EtoT3dr4eOWO",
71
+ "charlie": "IKne3meq5aSn9XLyUdCD",
72
+ "charlotte": "XB0fDUnXU5powFXDhCwa",
73
+ "chris": "iP95p4xoKVk53GoZ742B",
74
+ "daniel": "onwK4e9ZLuTAKqWW03F9",
75
+ "eric": "cjVigY5qzO86Huf0OWal",
76
+ "george": "JBFqnCBsd6RMkjVDRZzb",
77
+ "jessica": "cgSgspJ2msm6clMCkdW9",
78
+ "laura": "FGY2WhTYpPnrIDTdsKH5",
79
+ "liam": "TX3LPaxmHKxFdv7VOQHJ",
80
+ "lily": "pFZP5JQG7iQjIQuC4Bku",
81
+ "matilda": "XrExE9yKIg1WjnnlVkGX",
82
+ "sarah": "EXAVITQu4vr4xnSDxMaL",
83
+ "will": "bIHbv24MWmeRgasZH58o"
84
+ }
85
+
86
+ # Legacy voice mapping for backward compatibility
87
+ all_voices: dict[str, str] = {
88
+ "Brian": "nPczCjzI2devNBz1zQrb", "Alice": "Xb7hH8MSUJpSbSDYk0k2", "Bill": "pqHfZKP75CvOlQylNhV4",
89
+ "Callum": "N2lVS1w4EtoT3dr4eOWO", "Charlie": "IKne3meq5aSn9XLyUdCD", "Charlotte": "XB0fDUnXU5powFXDhCwa",
90
+ "Chris": "iP95p4xoKVk53GoZ742B", "Daniel": "onwK4e9ZLuTAKqWW03F9", "Eric": "cjVigY5qzO86Huf0OWal",
91
+ "George": "JBFqnCBsd6RMkjVDRZzb", "Jessica": "cgSgspJ2msm6clMCkdW9", "Laura": "FGY2WhTYpPnrIDTdsKH5",
92
+ "Liam": "TX3LPaxmHKxFdv7VOQHJ", "Lily": "pFZP5JQG7iQjIQuC4Bku", "Matilda": "XrExE9yKIg1WjnnlVkGX",
93
+ "Sarah": "EXAVITQu4vr4xnSDxMaL", "Will": "bIHbv24MWmeRgasZH58o"
94
+ }
95
+
96
+ def __init__(self, timeout: int = 20, proxies: dict = None):
97
+ """
98
+ Initialize the ElevenLabs TTS client.
99
+
100
+ Args:
101
+ timeout (int): Request timeout in seconds
102
+ proxies (dict): Proxy configuration
103
+ """
104
+ super().__init__()
105
+ self.api_url = "https://api.elevenlabs.io/v1/text-to-speech"
106
+ self.session = requests.Session()
107
+ self.session.headers.update(self.headers)
108
+ if proxies:
109
+ self.session.proxies.update(proxies)
110
+ self.timeout = timeout
111
+ self.params = {'allow_unauthenticated': '1'}
112
+ self.default_voice = "brian"
113
+
114
+ def tts(
115
+ self,
116
+ text: str,
117
+ model: str = "gpt-4o-mini-tts",
118
+ voice: str = "brian",
119
+ response_format: str = "mp3",
120
+ instructions: str = None,
121
+ verbose: bool = True
122
+ ) -> str:
123
+ """
124
+ Convert text to speech using ElevenLabs API with OpenAI-compatible parameters.
125
+
126
+ Args:
127
+ text (str): The text to convert to speech (max 10,000 characters)
128
+ model (str): The TTS model to use (gpt-4o-mini-tts, tts-1, tts-1-hd)
129
+ voice (str): The voice to use for TTS (brian, alice, bill, etc.)
130
+ response_format (str): Audio format (mp3, opus, aac, flac, wav, pcm)
131
+ instructions (str): Voice instructions (not used by ElevenLabs but kept for compatibility)
132
+ verbose (bool): Whether to print debug information
133
+
134
+ Returns:
135
+ str: Path to the generated audio file
136
+
137
+ Raises:
138
+ ValueError: If input parameters are invalid
139
+ exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio
140
+ """
141
+ # Validate input parameters
142
+ if not text or not isinstance(text, str):
143
+ raise ValueError("Input text must be a non-empty string")
144
+ if len(text) > 10000:
145
+ raise ValueError("Input text exceeds maximum allowed length of 10,000 characters")
146
+
147
+ # Validate model, voice, and format using base class methods
148
+ model = self.validate_model(model)
149
+ voice = self.validate_voice(voice)
150
+ response_format = self.validate_format(response_format)
151
+
152
+ # Map voice to ElevenLabs API format
153
+ elevenlabs_voice = self.voice_mapping.get(voice, voice)
154
+
155
+ # Create temporary file with appropriate extension
156
+ file_extension = f".{response_format}" if response_format != "pcm" else ".wav"
157
+ filename = pathlib.Path(tempfile.mktemp(suffix=file_extension, dir=self.temp_dir))
158
+
159
+ # Split text into sentences using the utils module for better processing
160
+ sentences = utils.split_sentences(text)
161
+ if verbose:
162
+ print(f"[debug] Processing {len(sentences)} sentences")
163
+ print(f"[debug] Model: {model}")
164
+ print(f"[debug] Voice: {voice} -> {elevenlabs_voice}")
165
+ print(f"[debug] Format: {response_format}")
166
+
167
+ def generate_audio_for_chunk(part_text: str, part_number: int):
168
+ """
169
+ Generate audio for a single chunk of text.
170
+
171
+ Args:
172
+ part_text (str): The text chunk to convert
173
+ part_number (int): The chunk number for ordering
174
+
175
+ Returns:
176
+ tuple: (part_number, audio_data)
177
+
178
+ Raises:
179
+ requests.RequestException: If there's an API error
180
+ """
181
+ max_retries = 3
182
+ retry_count = 0
183
+
184
+ while retry_count < max_retries:
185
+ try:
186
+ json_data = {
187
+ 'text': part_text,
188
+ 'model_id': 'eleven_multilingual_v2',
189
+ # Add model parameter for future ElevenLabs API compatibility
190
+ 'tts_model': model
191
+ }
192
+ response = self.session.post(
193
+ url=f'{self.api_url}/{elevenlabs_voice}',
194
+ params=self.params,
195
+ headers=self.headers,
196
+ json=json_data,
197
+ timeout=self.timeout
198
+ )
199
+ response.raise_for_status()
200
+
201
+ # Check if the request was successful
202
+ if response.ok and response.status_code == 200:
203
+ if verbose:
204
+ print(f"[debug] Chunk {part_number} processed successfully")
205
+ return part_number, response.content
206
+ else:
207
+ if verbose:
208
+ print(f"[debug] No data received for chunk {part_number}. Retrying...")
209
+
210
+ except requests.RequestException as e:
211
+ if verbose:
212
+ print(f"[debug] Error for chunk {part_number}: {e}. Retrying...")
213
+ retry_count += 1
214
+ time.sleep(1)
215
+
216
+ raise exceptions.FailedToGenerateResponseError(f"Failed to generate audio for chunk {part_number} after {max_retries} attempts")
217
+
218
+ try:
219
+ # Using ThreadPoolExecutor to handle requests concurrently
220
+ with ThreadPoolExecutor() as executor:
221
+ futures = {
222
+ executor.submit(generate_audio_for_chunk, sentence.strip(), chunk_num): chunk_num
223
+ for chunk_num, sentence in enumerate(sentences, start=1)
224
+ }
225
+
226
+ # Dictionary to store results with order preserved
227
+ audio_chunks = {}
228
+
229
+ for future in as_completed(futures):
230
+ chunk_num = futures[future]
231
+ try:
232
+ part_number, audio_data = future.result()
233
+ audio_chunks[part_number] = audio_data # Store the audio data in correct sequence
234
+ except Exception as e:
235
+ raise exceptions.FailedToGenerateResponseError(f"Failed to generate audio for chunk {chunk_num}: {str(e)}")
236
+
237
+ # Combine all audio chunks in order
238
+ combined_audio = BytesIO()
239
+ for part_number in sorted(audio_chunks.keys()):
240
+ combined_audio.write(audio_chunks[part_number])
241
+ if verbose:
242
+ print(f"[debug] Added chunk {part_number} to the combined file.")
243
+
244
+ # Save the combined audio data to a single file
245
+ with open(filename, 'wb') as f:
246
+ f.write(combined_audio.getvalue())
247
+
248
+ if verbose:
249
+ print(f"[debug] Speech generated successfully")
250
+ print(f"[debug] Audio saved to {filename}")
251
+
252
+ return filename.as_posix()
253
+
254
+ except requests.exceptions.RequestException as e:
255
+ if verbose:
256
+ print(f"[debug] Failed to perform the operation: {e}")
257
+ raise exceptions.FailedToGenerateResponseError(
258
+ f"Failed to perform the operation: {e}"
259
+ )
260
+
261
+ def create_speech(
262
+ self,
263
+ input: str,
264
+ model: str = "gpt-4o-mini-tts",
265
+ voice: str = "brian",
266
+ response_format: str = "mp3",
267
+ instructions: str = None,
268
+ verbose: bool = False
269
+ ) -> str:
270
+ """
271
+ OpenAI-compatible speech creation interface.
272
+
273
+ Args:
274
+ input (str): The text to convert to speech
275
+ model (str): The TTS model to use
276
+ voice (str): The voice to use
277
+ response_format (str): Audio format
278
+ instructions (str): Voice instructions (not used by ElevenLabs)
279
+ verbose (bool): Whether to print debug information
280
+
281
+ Returns:
282
+ str: Path to the generated audio file
283
+ """
284
+ return self.tts(
285
+ text=input,
286
+ model=model,
287
+ voice=voice,
288
+ response_format=response_format,
289
+ instructions=instructions,
290
+ verbose=verbose
291
+ )
292
+
293
+ def with_streaming_response(self):
294
+ """
295
+ Return a streaming response context manager (OpenAI-compatible).
296
+
297
+ Returns:
298
+ StreamingResponseContextManager: Context manager for streaming responses
299
+ """
300
+ return StreamingResponseContextManager(self)
301
+
302
+
303
+ class StreamingResponseContextManager:
304
+ """
305
+ Context manager for streaming TTS responses (OpenAI-compatible).
306
+ """
307
+
308
+ def __init__(self, tts_provider: ElevenlabsTTS):
309
+ self.tts_provider = tts_provider
310
+ self.audio_file = None
311
+
312
+ def create(
313
+ self,
314
+ input: str,
315
+ model: str = "gpt-4o-mini-tts",
316
+ voice: str = "brian",
317
+ response_format: str = "mp3",
318
+ instructions: str = None
319
+ ):
320
+ """
321
+ Create speech with streaming capability.
322
+
323
+ Args:
324
+ input (str): The text to convert to speech
325
+ model (str): The TTS model to use
326
+ voice (str): The voice to use
327
+ response_format (str): Audio format
328
+ instructions (str): Voice instructions
329
+
330
+ Returns:
331
+ StreamingResponse: Streaming response object
332
+ """
333
+ self.audio_file = self.tts_provider.create_speech(
334
+ input=input,
335
+ model=model,
336
+ voice=voice,
337
+ response_format=response_format,
338
+ instructions=instructions
339
+ )
340
+ return StreamingResponse(self.audio_file)
341
+
342
+ def __enter__(self):
343
+ return self
344
+
345
+ def __exit__(self, exc_type, exc_val, exc_tb):
346
+ pass
347
+
348
+
349
+ class StreamingResponse:
350
+ """
351
+ Streaming response object for TTS audio (OpenAI-compatible).
352
+ """
353
+
354
+ def __init__(self, audio_file: str):
355
+ self.audio_file = audio_file
356
+
357
+ def __enter__(self):
358
+ """Enter the context manager."""
359
+ return self
360
+
361
+ def __exit__(self, exc_type, exc_val, exc_tb):
362
+ """Exit the context manager."""
363
+ pass
364
+
365
+ def stream_to_file(self, file_path: str, chunk_size: int = 1024):
366
+ """
367
+ Stream audio content to a file.
368
+
369
+ Args:
370
+ file_path (str): Destination file path
371
+ chunk_size (int): Size of chunks to read/write
372
+ """
373
+ import shutil
374
+ shutil.copy2(self.audio_file, file_path)
375
+
376
+ def iter_bytes(self, chunk_size: int = 1024):
377
+ """
378
+ Iterate over audio bytes in chunks.
379
+
380
+ Args:
381
+ chunk_size (int): Size of chunks to yield
382
+
383
+ Yields:
384
+ bytes: Audio data chunks
385
+ """
386
+ with open(self.audio_file, 'rb') as f:
387
+ while chunk := f.read(chunk_size):
388
+ yield chunk
389
+
390
+
391
+ # Example usage
392
+ if __name__ == "__main__":
393
+ # Example usage demonstrating OpenAI-compatible interface
394
+ elevenlabs = ElevenlabsTTS()
395
+
396
+ try:
397
+ # Basic usage
398
+ print("Testing basic speech generation...")
399
+ audio_file = elevenlabs.create_speech(
400
+ input="Today is a wonderful day to build something people love!",
401
+ model="gpt-4o-mini-tts",
402
+ voice="brian",
403
+ instructions="Speak in a cheerful and positive tone."
404
+ )
405
+ print(f"Audio file generated: {audio_file}")
406
+
407
+ # Streaming usage
408
+ print("\nTesting streaming response...")
409
+ with elevenlabs.with_streaming_response().create(
410
+ input="This is a streaming test with ElevenLabs voices.",
411
+ voice="alice",
412
+ response_format="wav"
413
+ ) as response:
414
+ response.stream_to_file("elevenlabs_streaming_test.wav")
415
+ print("Streaming audio saved to elevenlabs_streaming_test.wav")
416
+
417
+ # Legacy compatibility test
418
+ print("\nTesting legacy voice compatibility...")
419
+ legacy_audio = elevenlabs.tts("Testing legacy voice format.", voice="brian")
420
+ print(f"Legacy audio file generated: {legacy_audio}")
421
+
422
+ except exceptions.FailedToGenerateResponseError as e:
423
+ print(f"Error: {e}")
424
+ except Exception as e:
425
+ print(f"Unexpected error: {e}")