zach commited on
Commit
bf6610d
·
1 Parent(s): 4a475d2

Update hume integration to use Hume python sdk

Browse files
Files changed (4) hide show
  1. pyproject.toml +3 -2
  2. src/assets/styles.css +1 -1
  3. src/integrations/hume_api.py +60 -70
  4. uv.lock +68 -49
pyproject.toml CHANGED
@@ -8,15 +8,16 @@ dependencies = [
8
  "anthropic>=0.45.2",
9
  "asyncpg>=0.28.0",
10
  "elevenlabs>=1.50.7",
11
- "gradio>=5.15.0",
12
  "greenlet>=2.0.0",
13
- "httpx>=0.24.1",
14
  "python-dotenv>=1.0.1",
15
  "sqlalchemy>=2.0.0",
16
  "tenacity>=9.0.0",
17
  ]
18
 
19
  [tool.uv]
 
20
  dev-dependencies = [
21
  "mypy>=1.15.0",
22
  "pre-commit>=4.1.0",
 
8
  "anthropic>=0.45.2",
9
  "asyncpg>=0.28.0",
10
  "elevenlabs>=1.50.7",
11
+ "gradio>=5.18.0",
12
  "greenlet>=2.0.0",
13
+ "hume>=0.7.8",
14
  "python-dotenv>=1.0.1",
15
  "sqlalchemy>=2.0.0",
16
  "tenacity>=9.0.0",
17
  ]
18
 
19
  [tool.uv]
20
+ override-dependencies = ["aiofiles==24.1.0"]
21
  dev-dependencies = [
22
  "mypy>=1.15.0",
23
  "pre-commit>=4.1.0",
src/assets/styles.css CHANGED
@@ -1,5 +1,5 @@
1
  /* Remove Gradio footer from UI */
2
- footer.svelte-sar7eh {
3
  display: none !important;
4
  }
5
 
 
1
  /* Remove Gradio footer from UI */
2
+ footer.svelte-1byz9vf {
3
  display: none !important;
4
  }
5
 
src/integrations/hume_api.py CHANGED
@@ -1,8 +1,8 @@
1
  """
2
  hume_api.py
3
 
4
- This file defines the interaction with the Hume text-to-speech (TTS) API.
5
- It includes functionality for API request handling and processing API responses.
6
 
7
  Key Features:
8
  - Encapsulates all logic related to the Hume TTS API.
@@ -15,10 +15,14 @@ Key Features:
15
  import logging
16
  import time
17
  from dataclasses import dataclass, field
18
- from typing import Any, Dict, Literal, Tuple, Union
19
 
20
  # Third-Party Library Imports
21
- import httpx
 
 
 
 
22
  from tenacity import after_log, before_log, retry, retry_if_exception, stop_after_attempt, wait_exponential
23
 
24
  # Local Application Imports
@@ -26,34 +30,22 @@ from src.config import Config, logger
26
  from src.constants import CLIENT_ERROR_CODE, SERVER_ERROR_CODE
27
  from src.utils import save_base64_audio_to_file, validate_env_var
28
 
29
- HumeSupportedFileFormat = Literal["mp3", "pcm", "wav"]
30
- """Supported audio file formats for the Hume TTS API"""
31
-
32
 
33
  @dataclass(frozen=True)
34
  class HumeConfig:
35
  """Immutable configuration for interacting with the Hume TTS API."""
36
 
37
  api_key: str = field(init=False)
38
- headers: Dict[str, str] = field(init=False)
39
- url: str = "https://api.hume.ai/v0/tts/octave"
40
- file_format: HumeSupportedFileFormat = "mp3"
41
  request_timeout: float = 40.0
42
 
43
  def __post_init__(self) -> None:
44
- # Validate required attributes.
45
- if not self.url:
46
- raise ValueError("Hume TTS endpoint URL is not set.")
47
  if not self.file_format:
48
  raise ValueError("Hume TTS file format is not set.")
49
 
50
  computed_api_key = validate_env_var("HUME_API_KEY")
51
  object.__setattr__(self, "api_key", computed_api_key)
52
- computed_headers = {
53
- "X-Hume-Api-Key": f"{computed_api_key}",
54
- "Content-Type": "application/json",
55
- }
56
- object.__setattr__(self, "headers", computed_headers)
57
 
58
 
59
  class HumeError(Exception):
@@ -91,10 +83,10 @@ async def text_to_speech_with_hume(
91
  """
92
  Asynchronously synthesizes speech using the Hume TTS API, processes audio data, and writes audio to a file.
93
 
94
- This function sends a POST request to the Hume TTS API with a character description and text to be converted to
95
- speech. Depending on the specified number of generations (1 or 2), the API returns one or two generations.
96
- For each generation, the function extracts the base64-encoded audio and generation ID, saves the audio as an MP3
97
- file, and returns the relevant details.
98
 
99
  Args:
100
  character_description (str): Description used for voice synthesis.
@@ -122,27 +114,32 @@ async def text_to_speech_with_hume(
122
  raise ValueError("Invalid number of generations specified. Must be 1 or 2.")
123
 
124
  hume_config = config.hume_config
125
- request_body = {
126
- "utterances": [{"text": text, "description": character_description or None}],
127
- "format": {"type": hume_config.file_format},
128
- "num_generations": num_generations,
129
- }
130
 
131
  start_time = time.time()
132
  try:
133
- async with httpx.AsyncClient() as client:
134
- response = await client.post(
135
- url=hume_config.url,
136
- headers=hume_config.headers,
137
- json=request_body,
138
- timeout=hume_config.request_timeout,
139
- )
140
- elapsed_time = time.time() - start_time
141
- logger.info(f"Hume API request completed in {elapsed_time:.2f} seconds")
142
- response.raise_for_status()
143
- response_data = response.json()
144
-
145
- generations = response_data.get("generations")
 
 
 
 
 
 
 
 
 
 
146
  if not generations:
147
  msg = "No generations returned by Hume API."
148
  logger.error(msg)
@@ -158,22 +155,19 @@ async def text_to_speech_with_hume(
158
  generation_b_id, audio_b_path = _parse_hume_tts_generation(generation_b, config)
159
  return (generation_a_id, audio_a_path, generation_b_id, audio_b_path)
160
 
161
- except (httpx.ReadTimeout, httpx.ConnectTimeout, httpx.ConnectError) as e:
 
162
  logger.error(f"Hume API request failed after {elapsed_time:.2f} seconds: {e!s}")
163
- raise HumeError(
164
- message=f"Connection to Hume API failed: {e!s}. Please try again later.",
165
- original_exception=e,
166
- ) from e
167
 
168
- except httpx.HTTPStatusError as e:
169
- if e.response is not None and CLIENT_ERROR_CODE <= e.response.status_code < SERVER_ERROR_CODE:
170
- error_message = f"HTTP Error {e.response.status_code}: {e.response.text}"
171
- logger.error(error_message)
172
- raise UnretryableHumeError(
173
- message=error_message,
174
- original_exception=e,
175
- ) from e
176
- error_message = f"HTTP Error {e.response.status_code if e.response else 'unknown'}"
177
  logger.error(error_message)
178
  raise HumeError(
179
  message=error_message,
@@ -184,34 +178,30 @@ async def text_to_speech_with_hume(
184
  error_type = type(e).__name__
185
  error_message = str(e) if str(e) else f"An error of type {error_type} occurred"
186
  logger.error("Error during Hume API call: %s - %s", error_type, error_message)
187
- raise HumeError(
188
- message=error_message,
189
- original_exception=e,
190
- ) from e
191
 
192
- def _parse_hume_tts_generation(generation: Dict[str, Any], config: Config) -> Tuple[str, str]:
193
  """
194
  Parses a Hume TTS generation response and saves the decoded audio as an MP3 file.
195
 
196
  Args:
197
- generation (Dict[str, Any]): TTS generation response containing 'generation_id' and 'audio'.
198
  config (Config): Application configuration for saving the audio file.
199
 
200
  Returns:
201
  Tuple[str, str]: (generation_id, audio_path)
202
 
203
  Raises:
204
- KeyError: If expected keys are missing.
205
  Exception: Propagates exceptions from saving the audio file.
206
  """
207
- generation_id = generation.get("generation_id")
208
- if generation_id is None:
209
- raise KeyError("The generation dictionary is missing the 'generation_id' key.")
210
 
211
- base64_audio = generation.get("audio")
212
- if base64_audio is None:
213
- raise KeyError("The generation dictionary is missing the 'audio' key.")
214
 
215
- filename = f"{generation_id}.mp3"
216
- audio_file_path = save_base64_audio_to_file(base64_audio, filename, config)
217
- return generation_id, audio_file_path
 
1
  """
2
  hume_api.py
3
 
4
+ This file defines the interaction with the Hume text-to-speech (TTS) API using the
5
+ Hume Python SDK. It includes functionality for API request handling and processing API responses.
6
 
7
  Key Features:
8
  - Encapsulates all logic related to the Hume TTS API.
 
15
  import logging
16
  import time
17
  from dataclasses import dataclass, field
18
+ from typing import Tuple, Union
19
 
20
  # Third-Party Library Imports
21
+ from hume import AsyncHumeClient
22
+ from hume.core.api_error import ApiError
23
+ from hume.tts import PostedUtterance
24
+ from hume.tts.types import ReturnGeneration, ReturnTts
25
+ from hume.tts.types.format import Format, FormatMp3
26
  from tenacity import after_log, before_log, retry, retry_if_exception, stop_after_attempt, wait_exponential
27
 
28
  # Local Application Imports
 
30
  from src.constants import CLIENT_ERROR_CODE, SERVER_ERROR_CODE
31
  from src.utils import save_base64_audio_to_file, validate_env_var
32
 
 
 
 
33
 
34
  @dataclass(frozen=True)
35
  class HumeConfig:
36
  """Immutable configuration for interacting with the Hume TTS API."""
37
 
38
  api_key: str = field(init=False)
39
+ file_format: Format = field(default_factory=FormatMp3)
 
 
40
  request_timeout: float = 40.0
41
 
42
  def __post_init__(self) -> None:
43
+ """Validate required attributes and set computed fields."""
 
 
44
  if not self.file_format:
45
  raise ValueError("Hume TTS file format is not set.")
46
 
47
  computed_api_key = validate_env_var("HUME_API_KEY")
48
  object.__setattr__(self, "api_key", computed_api_key)
 
 
 
 
 
49
 
50
 
51
  class HumeError(Exception):
 
83
  """
84
  Asynchronously synthesizes speech using the Hume TTS API, processes audio data, and writes audio to a file.
85
 
86
+ This function uses the Hume Python SDK to send a request to the Hume TTS API with a character description
87
+ and text to be converted to speech. Depending on the specified number of generations (1 or 2), the API
88
+ returns one or two generations. For each generation, the function extracts the base64-encoded audio
89
+ and generation ID, saves the audio as an MP3 file, and returns the relevant details.
90
 
91
  Args:
92
  character_description (str): Description used for voice synthesis.
 
114
  raise ValueError("Invalid number of generations specified. Must be 1 or 2.")
115
 
116
  hume_config = config.hume_config
 
 
 
 
 
117
 
118
  start_time = time.time()
119
  try:
120
+ # Initialize the client for this request
121
+ hume_client = AsyncHumeClient(
122
+ api_key=hume_config.api_key,
123
+ timeout=hume_config.request_timeout
124
+ )
125
+
126
+ # Create the utterance with the character description and text
127
+ utterance = PostedUtterance(
128
+ text=text,
129
+ description=character_description or None
130
+ )
131
+
132
+ # Call the TTS API through the SDK
133
+ response: ReturnTts = await hume_client.tts.synthesize_json(
134
+ utterances=[utterance],
135
+ format=hume_config.file_format,
136
+ num_generations=num_generations
137
+ )
138
+
139
+ elapsed_time = time.time() - start_time
140
+ logger.info(f"Hume API request completed in {elapsed_time:.2f} seconds")
141
+
142
+ generations = response.generations
143
  if not generations:
144
  msg = "No generations returned by Hume API."
145
  logger.error(msg)
 
155
  generation_b_id, audio_b_path = _parse_hume_tts_generation(generation_b, config)
156
  return (generation_a_id, audio_a_path, generation_b_id, audio_b_path)
157
 
158
+ except ApiError as e:
159
+ elapsed_time = time.time() - start_time
160
  logger.error(f"Hume API request failed after {elapsed_time:.2f} seconds: {e!s}")
 
 
 
 
161
 
162
+ if hasattr(e, 'status_code') and e.status_code is not None:
163
+ if CLIENT_ERROR_CODE <= e.status_code < SERVER_ERROR_CODE:
164
+ error_message = f"HTTP Error {e.status_code}: {e!s}"
165
+ logger.error(error_message)
166
+ raise UnretryableHumeError(message=error_message, original_exception=e) from e
167
+ error_message = f"HTTP Error {e.status_code}: {e!s}"
168
+ else:
169
+ error_message = str(e)
170
+
171
  logger.error(error_message)
172
  raise HumeError(
173
  message=error_message,
 
178
  error_type = type(e).__name__
179
  error_message = str(e) if str(e) else f"An error of type {error_type} occurred"
180
  logger.error("Error during Hume API call: %s - %s", error_type, error_message)
181
+ raise HumeError(message=error_message, original_exception=e) from e
182
+
 
 
183
 
184
+ def _parse_hume_tts_generation(generation: ReturnGeneration, config: Config) -> Tuple[str, str]:
185
  """
186
  Parses a Hume TTS generation response and saves the decoded audio as an MP3 file.
187
 
188
  Args:
189
+ generation (ReturnGeneration): TTS generation response containing generation_id and audio.
190
  config (Config): Application configuration for saving the audio file.
191
 
192
  Returns:
193
  Tuple[str, str]: (generation_id, audio_path)
194
 
195
  Raises:
196
+ KeyError: If expected attributes are missing.
197
  Exception: Propagates exceptions from saving the audio file.
198
  """
199
+ if not generation.generation_id:
200
+ raise KeyError("The generation is missing the generation_id.")
 
201
 
202
+ if not generation.audio:
203
+ raise KeyError("The generation is missing the audio data.")
 
204
 
205
+ filename = f"{generation.generation_id}.mp3"
206
+ audio_file_path = save_base64_audio_to_file(generation.audio, filename, config)
207
+ return generation.generation_id, audio_file_path
uv.lock CHANGED
@@ -6,13 +6,16 @@ resolution-markers = [
6
  "python_full_version >= '3.13'",
7
  ]
8
 
 
 
 
9
  [[package]]
10
  name = "aiofiles"
11
- version = "23.2.1"
12
  source = { registry = "https://pypi.org/simple" }
13
- sdist = { url = "https://files.pythonhosted.org/packages/af/41/cfed10bc64d774f497a86e5ede9248e1d062db675504b41c320954d99641/aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a", size = 32072 }
14
  wheels = [
15
- { url = "https://files.pythonhosted.org/packages/c5/19/5af6804c4cc0fed83f47bff6e413a98a36618e7d40185cd36e69737f3b0e/aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107", size = 15727 },
16
  ]
17
 
18
  [[package]]
@@ -250,6 +253,15 @@ wheels = [
250
  { url = "https://files.pythonhosted.org/packages/39/92/6096e18b5d673195fb5f702f9d2cc881595d876659db4e025760faed9b42/elevenlabs-1.50.7-py3-none-any.whl", hash = "sha256:3b75a28e6e81d677d2525f51713167f521bc0dab062984d7a3d1e29411ae424a", size = 264723 },
251
  ]
252
 
 
 
 
 
 
 
 
 
 
253
  [[package]]
254
  name = "expressive-tts-arena"
255
  version = "0.1.0"
@@ -260,7 +272,7 @@ dependencies = [
260
  { name = "elevenlabs" },
261
  { name = "gradio" },
262
  { name = "greenlet" },
263
- { name = "httpx" },
264
  { name = "python-dotenv" },
265
  { name = "sqlalchemy" },
266
  { name = "tenacity" },
@@ -282,9 +294,9 @@ requires-dist = [
282
  { name = "anthropic", specifier = ">=0.45.2" },
283
  { name = "asyncpg", specifier = ">=0.28.0" },
284
  { name = "elevenlabs", specifier = ">=1.50.7" },
285
- { name = "gradio", specifier = ">=5.15.0" },
286
  { name = "greenlet", specifier = ">=2.0.0" },
287
- { name = "httpx", specifier = ">=0.24.1" },
288
  { name = "python-dotenv", specifier = ">=1.0.1" },
289
  { name = "sqlalchemy", specifier = ">=2.0.0" },
290
  { name = "tenacity", specifier = ">=9.0.0" },
@@ -344,7 +356,7 @@ wheels = [
344
 
345
  [[package]]
346
  name = "gradio"
347
- version = "5.15.0"
348
  source = { registry = "https://pypi.org/simple" }
349
  dependencies = [
350
  { name = "aiofiles" },
@@ -377,12 +389,12 @@ dependencies = [
377
  { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
378
  ]
379
  wheels = [
380
- { url = "https://files.pythonhosted.org/packages/a4/28/6a52bac8b13aca4f76baef03d5f840cc1f7486e879bc1ee19df51b7590d2/gradio-5.15.0-py3-none-any.whl", hash = "sha256:b0d72bf1e70c4a08283066c510d03bbdb9e378c1f806dd948e333c75bb22b3f1", size = 57766589 },
381
  ]
382
 
383
  [[package]]
384
  name = "gradio-client"
385
- version = "1.7.0"
386
  source = { registry = "https://pypi.org/simple" }
387
  dependencies = [
388
  { name = "fsspec" },
@@ -392,9 +404,9 @@ dependencies = [
392
  { name = "typing-extensions" },
393
  { name = "websockets" },
394
  ]
395
- sdist = { url = "https://files.pythonhosted.org/packages/c5/78/e5a4a2b0f4d1ba01ec4169e181a3134fc65b6360d40817070892c3557000/gradio_client-1.7.0.tar.gz", hash = "sha256:87f6ade197951f38bac0431b2a436a8ebb2f33b2ceba2ef8e1e5bef8d8b238e4", size = 320039 }
396
  wheels = [
397
- { url = "https://files.pythonhosted.org/packages/f3/c1/def2bd93b8beab342c443bf5ac47f85e48b78eca010bbff51d6978472a3f/gradio_client-1.7.0-py3-none-any.whl", hash = "sha256:b403570c67f121ebbbc19ac1f0afa2ab1bab085ce60d96eb190832fe871aa946", size = 321900 },
398
  ]
399
 
400
  [[package]]
@@ -494,6 +506,24 @@ wheels = [
494
  { url = "https://files.pythonhosted.org/packages/ea/da/6c2bea5327b640920267d3bf2c9fc114cfbd0a5de234d81cda80cc9e33c8/huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7", size = 464068 },
495
  ]
496
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  [[package]]
498
  name = "identify"
499
  version = "2.6.6"
@@ -1405,42 +1435,31 @@ wheels = [
1405
 
1406
  [[package]]
1407
  name = "websockets"
1408
- version = "14.2"
1409
- source = { registry = "https://pypi.org/simple" }
1410
- sdist = { url = "https://files.pythonhosted.org/packages/94/54/8359678c726243d19fae38ca14a334e740782336c9f19700858c4eb64a1e/websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5", size = 164394 }
1411
- wheels = [
1412
- { url = "https://files.pythonhosted.org/packages/15/b6/504695fb9a33df0ca56d157f5985660b5fc5b4bf8c78f121578d2d653392/websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166", size = 163088 },
1413
- { url = "https://files.pythonhosted.org/packages/81/26/ebfb8f6abe963c795122439c6433c4ae1e061aaedfc7eff32d09394afbae/websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f", size = 160745 },
1414
- { url = "https://files.pythonhosted.org/packages/a1/c6/1435ad6f6dcbff80bb95e8986704c3174da8866ddb751184046f5c139ef6/websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910", size = 160995 },
1415
- { url = "https://files.pythonhosted.org/packages/96/63/900c27cfe8be1a1f2433fc77cd46771cf26ba57e6bdc7cf9e63644a61863/websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c", size = 170543 },
1416
- { url = "https://files.pythonhosted.org/packages/00/8b/bec2bdba92af0762d42d4410593c1d7d28e9bfd952c97a3729df603dc6ea/websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473", size = 169546 },
1417
- { url = "https://files.pythonhosted.org/packages/6b/a9/37531cb5b994f12a57dec3da2200ef7aadffef82d888a4c29a0d781568e4/websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473", size = 169911 },
1418
- { url = "https://files.pythonhosted.org/packages/60/d5/a6eadba2ed9f7e65d677fec539ab14a9b83de2b484ab5fe15d3d6d208c28/websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56", size = 170183 },
1419
- { url = "https://files.pythonhosted.org/packages/76/57/a338ccb00d1df881c1d1ee1f2a20c9c1b5b29b51e9e0191ee515d254fea6/websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142", size = 169623 },
1420
- { url = "https://files.pythonhosted.org/packages/64/22/e5f7c33db0cb2c1d03b79fd60d189a1da044e2661f5fd01d629451e1db89/websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d", size = 169583 },
1421
- { url = "https://files.pythonhosted.org/packages/aa/2e/2b4662237060063a22e5fc40d46300a07142afe30302b634b4eebd717c07/websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a", size = 163969 },
1422
- { url = "https://files.pythonhosted.org/packages/94/a5/0cda64e1851e73fc1ecdae6f42487babb06e55cb2f0dc8904b81d8ef6857/websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b", size = 164408 },
1423
- { url = "https://files.pythonhosted.org/packages/c1/81/04f7a397653dc8bec94ddc071f34833e8b99b13ef1a3804c149d59f92c18/websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c", size = 163096 },
1424
- { url = "https://files.pythonhosted.org/packages/ec/c5/de30e88557e4d70988ed4d2eabd73fd3e1e52456b9f3a4e9564d86353b6d/websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967", size = 160758 },
1425
- { url = "https://files.pythonhosted.org/packages/e5/8c/d130d668781f2c77d106c007b6c6c1d9db68239107c41ba109f09e6c218a/websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990", size = 160995 },
1426
- { url = "https://files.pythonhosted.org/packages/a6/bc/f6678a0ff17246df4f06765e22fc9d98d1b11a258cc50c5968b33d6742a1/websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda", size = 170815 },
1427
- { url = "https://files.pythonhosted.org/packages/d8/b2/8070cb970c2e4122a6ef38bc5b203415fd46460e025652e1ee3f2f43a9a3/websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95", size = 169759 },
1428
- { url = "https://files.pythonhosted.org/packages/81/da/72f7caabd94652e6eb7e92ed2d3da818626e70b4f2b15a854ef60bf501ec/websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3", size = 170178 },
1429
- { url = "https://files.pythonhosted.org/packages/31/e0/812725b6deca8afd3a08a2e81b3c4c120c17f68c9b84522a520b816cda58/websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9", size = 170453 },
1430
- { url = "https://files.pythonhosted.org/packages/66/d3/8275dbc231e5ba9bb0c4f93144394b4194402a7a0c8ffaca5307a58ab5e3/websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267", size = 169830 },
1431
- { url = "https://files.pythonhosted.org/packages/a3/ae/e7d1a56755ae15ad5a94e80dd490ad09e345365199600b2629b18ee37bc7/websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe", size = 169824 },
1432
- { url = "https://files.pythonhosted.org/packages/b6/32/88ccdd63cb261e77b882e706108d072e4f1c839ed723bf91a3e1f216bf60/websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205", size = 163981 },
1433
- { url = "https://files.pythonhosted.org/packages/b3/7d/32cdb77990b3bdc34a306e0a0f73a1275221e9a66d869f6ff833c95b56ef/websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce", size = 164421 },
1434
- { url = "https://files.pythonhosted.org/packages/82/94/4f9b55099a4603ac53c2912e1f043d6c49d23e94dd82a9ce1eb554a90215/websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e", size = 163102 },
1435
- { url = "https://files.pythonhosted.org/packages/8e/b7/7484905215627909d9a79ae07070057afe477433fdacb59bf608ce86365a/websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad", size = 160766 },
1436
- { url = "https://files.pythonhosted.org/packages/a3/a4/edb62efc84adb61883c7d2c6ad65181cb087c64252138e12d655989eec05/websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03", size = 160998 },
1437
- { url = "https://files.pythonhosted.org/packages/f5/79/036d320dc894b96af14eac2529967a6fc8b74f03b83c487e7a0e9043d842/websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f", size = 170780 },
1438
- { url = "https://files.pythonhosted.org/packages/63/75/5737d21ee4dd7e4b9d487ee044af24a935e36a9ff1e1419d684feedcba71/websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5", size = 169717 },
1439
- { url = "https://files.pythonhosted.org/packages/2c/3c/bf9b2c396ed86a0b4a92ff4cdaee09753d3ee389be738e92b9bbd0330b64/websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a", size = 170155 },
1440
- { url = "https://files.pythonhosted.org/packages/75/2d/83a5aca7247a655b1da5eb0ee73413abd5c3a57fc8b92915805e6033359d/websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20", size = 170495 },
1441
- { url = "https://files.pythonhosted.org/packages/79/dd/699238a92761e2f943885e091486378813ac8f43e3c84990bc394c2be93e/websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2", size = 169880 },
1442
- { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 },
1443
- { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 },
1444
- { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 },
1445
- { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 },
1446
  ]
 
6
  "python_full_version >= '3.13'",
7
  ]
8
 
9
+ [manifest]
10
+ overrides = [{ name = "aiofiles", specifier = "==24.1.0" }]
11
+
12
  [[package]]
13
  name = "aiofiles"
14
+ version = "24.1.0"
15
  source = { registry = "https://pypi.org/simple" }
16
+ sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 }
17
  wheels = [
18
+ { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 },
19
  ]
20
 
21
  [[package]]
 
253
  { url = "https://files.pythonhosted.org/packages/39/92/6096e18b5d673195fb5f702f9d2cc881595d876659db4e025760faed9b42/elevenlabs-1.50.7-py3-none-any.whl", hash = "sha256:3b75a28e6e81d677d2525f51713167f521bc0dab062984d7a3d1e29411ae424a", size = 264723 },
254
  ]
255
 
256
+ [[package]]
257
+ name = "eval-type-backport"
258
+ version = "0.2.2"
259
+ source = { registry = "https://pypi.org/simple" }
260
+ sdist = { url = "https://files.pythonhosted.org/packages/30/ea/8b0ac4469d4c347c6a385ff09dc3c048c2d021696664e26c7ee6791631b5/eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1", size = 9079 }
261
+ wheels = [
262
+ { url = "https://files.pythonhosted.org/packages/ce/31/55cd413eaccd39125368be33c46de24a1f639f2e12349b0361b4678f3915/eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a", size = 5830 },
263
+ ]
264
+
265
  [[package]]
266
  name = "expressive-tts-arena"
267
  version = "0.1.0"
 
272
  { name = "elevenlabs" },
273
  { name = "gradio" },
274
  { name = "greenlet" },
275
+ { name = "hume" },
276
  { name = "python-dotenv" },
277
  { name = "sqlalchemy" },
278
  { name = "tenacity" },
 
294
  { name = "anthropic", specifier = ">=0.45.2" },
295
  { name = "asyncpg", specifier = ">=0.28.0" },
296
  { name = "elevenlabs", specifier = ">=1.50.7" },
297
+ { name = "gradio", specifier = ">=5.18.0" },
298
  { name = "greenlet", specifier = ">=2.0.0" },
299
+ { name = "hume", specifier = ">=0.7.8" },
300
  { name = "python-dotenv", specifier = ">=1.0.1" },
301
  { name = "sqlalchemy", specifier = ">=2.0.0" },
302
  { name = "tenacity", specifier = ">=9.0.0" },
 
356
 
357
  [[package]]
358
  name = "gradio"
359
+ version = "5.18.0"
360
  source = { registry = "https://pypi.org/simple" }
361
  dependencies = [
362
  { name = "aiofiles" },
 
389
  { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
390
  ]
391
  wheels = [
392
+ { url = "https://files.pythonhosted.org/packages/e2/e4/1a9ec913da5480907d14d2c527278c6c09011d8ff370a1caa09cdb3f5d23/gradio-5.18.0-py3-none-any.whl", hash = "sha256:0ec8461de962a851a9c3dc75c6707dddd942f7b292dec425acc6d5dcfa685902", size = 62263574 },
393
  ]
394
 
395
  [[package]]
396
  name = "gradio-client"
397
+ version = "1.7.2"
398
  source = { registry = "https://pypi.org/simple" }
399
  dependencies = [
400
  { name = "fsspec" },
 
404
  { name = "typing-extensions" },
405
  { name = "websockets" },
406
  ]
407
+ sdist = { url = "https://files.pythonhosted.org/packages/65/b8/682e1868c47ce723fece65ae71405a560a8455d1e06bd70d4f60c1e6ac03/gradio_client-1.7.2.tar.gz", hash = "sha256:e3f1e1a63292a06a22a748719849bc6dbacdc89645f089c219f3d7a24e68d183", size = 320199 }
408
  wheels = [
409
+ { url = "https://files.pythonhosted.org/packages/95/cb/002424d4f5af1425f9cfe7dcee3ed795ed1367bf0f185a6c4bf81385e1d6/gradio_client-1.7.2-py3-none-any.whl", hash = "sha256:50d61b4db3e87639430a121a7cde4303055486ed72a5035edae94b4fbe6a0e6b", size = 322052 },
410
  ]
411
 
412
  [[package]]
 
506
  { url = "https://files.pythonhosted.org/packages/ea/da/6c2bea5327b640920267d3bf2c9fc114cfbd0a5de234d81cda80cc9e33c8/huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7", size = 464068 },
507
  ]
508
 
509
+ [[package]]
510
+ name = "hume"
511
+ version = "0.7.8"
512
+ source = { registry = "https://pypi.org/simple" }
513
+ dependencies = [
514
+ { name = "aiofiles" },
515
+ { name = "eval-type-backport" },
516
+ { name = "httpx" },
517
+ { name = "pydantic" },
518
+ { name = "pydantic-core" },
519
+ { name = "typing-extensions" },
520
+ { name = "websockets" },
521
+ ]
522
+ sdist = { url = "https://files.pythonhosted.org/packages/73/22/71dbdb36974a87433478347dd0ce124d3ea1f933f1246d252fcbd11e6696/hume-0.7.8.tar.gz", hash = "sha256:b2acfc93bde001c1de4e8c122db5fecd3db48f9c88d3771b393981bb5124eedc", size = 131970 }
523
+ wheels = [
524
+ { url = "https://files.pythonhosted.org/packages/68/29/b365a2b6bbfa229fc815c6b04ed444ee560b7720b25cc1278d4a0789c700/hume-0.7.8-py3-none-any.whl", hash = "sha256:50ef51053e4e1a69ed7efd489e23cc053ebbd8e10bbc06cf1de96631f2f7a732", size = 320580 },
525
+ ]
526
+
527
  [[package]]
528
  name = "identify"
529
  version = "2.6.6"
 
1435
 
1436
  [[package]]
1437
  name = "websockets"
1438
+ version = "12.0"
1439
+ source = { registry = "https://pypi.org/simple" }
1440
+ sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994 }
1441
+ wheels = [
1442
+ { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047 },
1443
+ { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282 },
1444
+ { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325 },
1445
+ { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502 },
1446
+ { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491 },
1447
+ { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872 },
1448
+ { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318 },
1449
+ { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594 },
1450
+ { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191 },
1451
+ { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453 },
1452
+ { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971 },
1453
+ { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061 },
1454
+ { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296 },
1455
+ { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326 },
1456
+ { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807 },
1457
+ { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751 },
1458
+ { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176 },
1459
+ { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246 },
1460
+ { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466 },
1461
+ { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083 },
1462
+ { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460 },
1463
+ { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985 },
1464
+ { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370 },
 
 
 
 
 
 
 
 
 
 
 
1465
  ]