xiaogpt 3.3__py3-none-any.whl → 3.5__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.
xiaogpt/cli.py CHANGED
@@ -111,9 +111,10 @@ def main():
111
111
  )
112
112
  parser.add_argument(
113
113
  "--verbose",
114
+ "-v",
114
115
  dest="verbose",
115
- action="store_true",
116
- default=None,
116
+ action="count",
117
+ default=0,
117
118
  help="show info",
118
119
  )
119
120
  parser.add_argument(
xiaogpt/config.py CHANGED
@@ -77,7 +77,7 @@ class Config:
77
77
  api_base: str | None = None
78
78
  deployment_id: str | None = None
79
79
  use_command: bool = False
80
- verbose: bool = False
80
+ verbose: int = 0
81
81
  start_conversation: str = "开始持续对话"
82
82
  end_conversation: str = "结束持续对话"
83
83
  stream: bool = False
xiaogpt/tts/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from xiaogpt.tts.base import TTS
2
+ from xiaogpt.tts.file import TetosFileTTS
3
+ from xiaogpt.tts.live import TetosLiveTTS
2
4
  from xiaogpt.tts.mi import MiTTS
3
- from xiaogpt.tts.tetos import TetosTTS
4
5
 
5
- __all__ = ["TTS", "TetosTTS", "MiTTS"]
6
+ __all__ = ["TTS", "TetosFileTTS", "MiTTS", "TetosLiveTTS"]
xiaogpt/tts/base.py CHANGED
@@ -2,20 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import abc
4
4
  import asyncio
5
- import functools
6
5
  import json
7
6
  import logging
8
- import os
9
- import random
10
- import socket
11
- import tempfile
12
- import threading
13
- from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
14
- from pathlib import Path
15
7
  from typing import TYPE_CHECKING, AsyncIterator
16
8
 
17
- from xiaogpt.utils import get_hostname
18
-
19
9
  if TYPE_CHECKING:
20
10
  from typing import TypeVar
21
11
 
@@ -46,7 +36,7 @@ class TTS(abc.ABC):
46
36
  break
47
37
  await asyncio.sleep(1)
48
38
 
49
- async def get_if_xiaoai_is_playing(self):
39
+ async def get_if_xiaoai_is_playing(self) -> bool:
50
40
  playing_info = await self.mina_service.player_get_status(self.device_id)
51
41
  # WTF xiaomi api
52
42
  is_playing = (
@@ -59,82 +49,3 @@ class TTS(abc.ABC):
59
49
  async def synthesize(self, lang: str, text_stream: AsyncIterator[str]) -> None:
60
50
  """Synthesize speech from a stream of text."""
61
51
  raise NotImplementedError
62
-
63
-
64
- class HTTPRequestHandler(SimpleHTTPRequestHandler):
65
- def log_message(self, format, *args):
66
- logger.debug(f"{self.address_string()} - {format}", *args)
67
-
68
- def log_error(self, format, *args):
69
- logger.error(f"{self.address_string()} - {format}", *args)
70
-
71
- def copyfile(self, source, outputfile):
72
- try:
73
- super().copyfile(source, outputfile)
74
- except (socket.error, ConnectionResetError, BrokenPipeError):
75
- # ignore this or TODO find out why the error later
76
- pass
77
-
78
-
79
- class AudioFileTTS(TTS):
80
- """A TTS model that generates audio files locally and plays them via URL."""
81
-
82
- def __init__(
83
- self, mina_service: MiNAService, device_id: str, config: Config
84
- ) -> None:
85
- super().__init__(mina_service, device_id, config)
86
- self.dirname = tempfile.TemporaryDirectory(prefix="xiaogpt-tts-")
87
- self._start_http_server()
88
-
89
- @abc.abstractmethod
90
- async def make_audio_file(self, lang: str, text: str) -> tuple[Path, float]:
91
- """Synthesize speech from text and save it to a file.
92
- Return the file path and the duration of the audio in seconds.
93
- The file path must be relative to the self.dirname.
94
- """
95
- raise NotImplementedError
96
-
97
- async def synthesize(self, lang: str, text_stream: AsyncIterator[str]) -> None:
98
- queue: asyncio.Queue[tuple[str, float]] = asyncio.Queue()
99
- finished = asyncio.Event()
100
-
101
- async def worker():
102
- async for text in text_stream:
103
- path, duration = await self.make_audio_file(lang, text)
104
- url = f"http://{self.hostname}:{self.port}/{path.name}"
105
- await queue.put((url, duration))
106
- finished.set()
107
-
108
- task = asyncio.create_task(worker())
109
-
110
- while True:
111
- try:
112
- url, duration = queue.get_nowait()
113
- except asyncio.QueueEmpty:
114
- if finished.is_set():
115
- break
116
- else:
117
- await asyncio.sleep(0.1)
118
- continue
119
- logger.debug("Playing URL %s (%s seconds)", url, duration)
120
- await asyncio.gather(
121
- self.mina_service.play_by_url(self.device_id, url, _type=1),
122
- self.wait_for_duration(duration),
123
- )
124
- await task
125
-
126
- def _start_http_server(self):
127
- # set the port range
128
- port_range = range(8050, 8090)
129
- # get a random port from the range
130
- self.port = int(os.getenv("XIAOGPT_PORT", random.choice(port_range)))
131
- # create the server
132
- handler = functools.partial(HTTPRequestHandler, directory=self.dirname.name)
133
- httpd = ThreadingHTTPServer(("", self.port), handler)
134
- # start the server in a new thread
135
- server_thread = threading.Thread(target=httpd.serve_forever)
136
- server_thread.daemon = True
137
- server_thread.start()
138
-
139
- self.hostname = get_hostname()
140
- logger.info(f"Serving on {self.hostname}:{self.port}")
xiaogpt/tts/file.py ADDED
@@ -0,0 +1,103 @@
1
+ import asyncio
2
+ import functools
3
+ import os
4
+ import random
5
+ import socket
6
+ import tempfile
7
+ import threading
8
+ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
9
+ from pathlib import Path
10
+ from typing import AsyncIterator
11
+
12
+ from miservice import MiNAService
13
+
14
+ from xiaogpt.config import Config
15
+ from xiaogpt.tts.base import TTS, logger
16
+ from xiaogpt.utils import get_hostname
17
+
18
+
19
+ class HTTPRequestHandler(SimpleHTTPRequestHandler):
20
+ def log_message(self, format, *args):
21
+ logger.debug(f"{self.address_string()} - {format}", *args)
22
+
23
+ def log_error(self, format, *args):
24
+ logger.error(f"{self.address_string()} - {format}", *args)
25
+
26
+ def copyfile(self, source, outputfile):
27
+ try:
28
+ super().copyfile(source, outputfile)
29
+ except (socket.error, ConnectionResetError, BrokenPipeError):
30
+ # ignore this or TODO find out why the error later
31
+ pass
32
+
33
+
34
+ class TetosFileTTS(TTS):
35
+ """A TTS model that generates audio files locally and plays them via URL."""
36
+
37
+ def __init__(
38
+ self, mina_service: MiNAService, device_id: str, config: Config
39
+ ) -> None:
40
+ from tetos import get_speaker
41
+
42
+ super().__init__(mina_service, device_id, config)
43
+ self.dirname = tempfile.TemporaryDirectory(prefix="xiaogpt-tts-")
44
+ self._start_http_server()
45
+
46
+ assert config.tts and config.tts != "mi"
47
+ speaker_cls = get_speaker(config.tts)
48
+ try:
49
+ self.speaker = speaker_cls(**config.tts_options)
50
+ except TypeError as e:
51
+ raise ValueError(f"{e}. Please add them via `tts_options` config") from e
52
+
53
+ async def make_audio_file(self, lang: str, text: str) -> tuple[Path, float]:
54
+ output_file = tempfile.NamedTemporaryFile(
55
+ suffix=".mp3", mode="wb", delete=False, dir=self.dirname.name
56
+ )
57
+ duration = await self.speaker.synthesize(text, output_file.name, lang=lang)
58
+ return Path(output_file.name), duration
59
+
60
+ async def synthesize(self, lang: str, text_stream: AsyncIterator[str]) -> None:
61
+ queue: asyncio.Queue[tuple[str, float]] = asyncio.Queue()
62
+ finished = asyncio.Event()
63
+
64
+ async def worker():
65
+ async for text in text_stream:
66
+ path, duration = await self.make_audio_file(lang, text)
67
+ url = f"http://{self.hostname}:{self.port}/{path.name}"
68
+ await queue.put((url, duration))
69
+ finished.set()
70
+
71
+ task = asyncio.create_task(worker())
72
+
73
+ while True:
74
+ try:
75
+ url, duration = queue.get_nowait()
76
+ except asyncio.QueueEmpty:
77
+ if finished.is_set():
78
+ break
79
+ else:
80
+ await asyncio.sleep(0.1)
81
+ continue
82
+ logger.debug("Playing URL %s (%s seconds)", url, duration)
83
+ await asyncio.gather(
84
+ self.mina_service.play_by_url(self.device_id, url, _type=1),
85
+ self.wait_for_duration(duration),
86
+ )
87
+ await task
88
+
89
+ def _start_http_server(self):
90
+ # set the port range
91
+ port_range = range(8050, 8090)
92
+ # get a random port from the range
93
+ self.port = int(os.getenv("XIAOGPT_PORT", random.choice(port_range)))
94
+ # create the server
95
+ handler = functools.partial(HTTPRequestHandler, directory=self.dirname.name)
96
+ httpd = ThreadingHTTPServer(("", self.port), handler)
97
+ # start the server in a new thread
98
+ server_thread = threading.Thread(target=httpd.serve_forever)
99
+ server_thread.daemon = True
100
+ server_thread.start()
101
+
102
+ self.hostname = get_hostname()
103
+ logger.info(f"Serving on {self.hostname}:{self.port}")
xiaogpt/tts/live.py ADDED
@@ -0,0 +1,103 @@
1
+ import asyncio
2
+ import os
3
+ import queue
4
+ import random
5
+ import threading
6
+ import uuid
7
+ from functools import lru_cache
8
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
9
+ from typing import AsyncIterator
10
+
11
+ from miservice import MiNAService
12
+
13
+ from xiaogpt.config import Config
14
+ from xiaogpt.tts.base import TTS, logger
15
+ from xiaogpt.utils import get_hostname
16
+
17
+
18
+ @lru_cache(maxsize=64)
19
+ def get_queue(key: str) -> queue.Queue[bytes]:
20
+ return queue.Queue()
21
+
22
+
23
+ class HTTPRequestHandler(BaseHTTPRequestHandler):
24
+ def do_GET(self):
25
+ self.send_response(200)
26
+ self.send_header("Content-type", "audio/mpeg")
27
+ self.end_headers()
28
+ key = self.path.split("/")[-1]
29
+ queue = get_queue(key)
30
+ chunks: list[bytes] = []
31
+ while True:
32
+ chunk = queue.get()
33
+ chunks.append(chunk)
34
+ if chunk == b"":
35
+ break
36
+ self.wfile.write(chunk)
37
+ for chunk in chunks:
38
+ queue.put_nowait(chunk)
39
+
40
+ def log_message(self, format, *args):
41
+ logger.debug(f"{self.address_string()} - {format}", *args)
42
+
43
+ def log_error(self, format, *args):
44
+ logger.error(f"{self.address_string()} - {format}", *args)
45
+
46
+
47
+ class TetosLiveTTS(TTS):
48
+ """A TTS model that generates audio in real-time."""
49
+
50
+ def __init__(
51
+ self, mina_service: MiNAService, device_id: str, config: Config
52
+ ) -> None:
53
+ from tetos import get_speaker
54
+
55
+ super().__init__(mina_service, device_id, config)
56
+ self._start_http_server()
57
+
58
+ assert config.tts and config.tts != "mi"
59
+ speaker_cls = get_speaker(config.tts)
60
+ try:
61
+ self.speaker = speaker_cls(**config.tts_options)
62
+ except TypeError as e:
63
+ raise ValueError(f"{e}. Please add them via `tts_options` config") from e
64
+ if not hasattr(self.speaker, "live"):
65
+ raise ValueError(f"{config.tts} Speaker does not support live synthesis")
66
+
67
+ async def synthesize(self, lang: str, text_stream: AsyncIterator[str]) -> None:
68
+ key = str(uuid.uuid4())
69
+ queue = get_queue(key)
70
+
71
+ async def worker():
72
+ async for chunk in self.speaker.live(text_stream, lang):
73
+ queue.put(chunk)
74
+ queue.put(b"")
75
+
76
+ task = asyncio.create_task(worker())
77
+ await self.mina_service.play_by_url(
78
+ self.device_id, f"http://{self.hostname}:{self.port}/{key}", _type=1
79
+ )
80
+
81
+ while True:
82
+ if await self.get_if_xiaoai_is_playing():
83
+ logger.debug("Xiaoai is playing, waiting")
84
+ await asyncio.sleep(1)
85
+ else:
86
+ break
87
+ await task
88
+
89
+ def _start_http_server(self):
90
+ # set the port range
91
+ port_range = range(8050, 8090)
92
+ # get a random port from the range
93
+ self.port = int(os.getenv("XIAOGPT_PORT", random.choice(port_range)))
94
+ # create the server
95
+ handler = HTTPRequestHandler
96
+ httpd = ThreadingHTTPServer(("", self.port), handler)
97
+ # start the server in a new thread
98
+ server_thread = threading.Thread(target=httpd.serve_forever)
99
+ server_thread.daemon = True
100
+ server_thread.start()
101
+
102
+ self.hostname = get_hostname()
103
+ logger.info(f"Serving on {self.hostname}:{self.port}")
xiaogpt/xiaogpt.py CHANGED
@@ -23,7 +23,8 @@ from xiaogpt.config import (
23
23
  WAKEUP_KEYWORD,
24
24
  Config,
25
25
  )
26
- from xiaogpt.tts import TTS, MiTTS, TetosTTS
26
+ from xiaogpt.tts import TTS, MiTTS, TetosFileTTS
27
+ from xiaogpt.tts.live import TetosLiveTTS
27
28
  from xiaogpt.utils import detect_language, parse_cookie_string
28
29
 
29
30
  EOF = object()
@@ -56,15 +57,20 @@ class MiGPT:
56
57
  async def poll_latest_ask(self):
57
58
  async with ClientSession() as session:
58
59
  session._cookie_jar = self.cookie_jar
60
+ log_polling = int(self.config.verbose) > 1
59
61
  while True:
60
- self.log.debug(
61
- "Listening new message, timestamp: %s", self.last_timestamp
62
- )
62
+ if log_polling:
63
+ self.log.debug(
64
+ "Listening new message, timestamp: %s", self.last_timestamp
65
+ )
63
66
  new_record = await self.get_latest_ask_from_xiaoai(session)
64
67
  start = time.perf_counter()
65
- self.log.debug(
66
- "Polling_event, timestamp: %s %s", self.last_timestamp, new_record
67
- )
68
+ if log_polling:
69
+ self.log.debug(
70
+ "Polling_event, timestamp: %s %s",
71
+ self.last_timestamp,
72
+ new_record,
73
+ )
68
74
  await self.polling_event.wait()
69
75
  if (
70
76
  self.config.mute_xiaoai
@@ -74,7 +80,10 @@ class MiGPT:
74
80
  await self.stop_if_xiaoai_is_playing()
75
81
  if (d := time.perf_counter() - start) < 1:
76
82
  # sleep to avoid too many request
77
- self.log.debug("Sleep %f, timestamp: %s", d, self.last_timestamp)
83
+ if log_polling:
84
+ self.log.debug(
85
+ "Sleep %f, timestamp: %s", d, self.last_timestamp
86
+ )
78
87
  # if you want force mute xiaoai, comment this line below.
79
88
  await asyncio.sleep(1 - d)
80
89
 
@@ -260,8 +269,10 @@ class MiGPT:
260
269
  def tts(self) -> TTS:
261
270
  if self.config.tts == "mi":
262
271
  return MiTTS(self.mina_service, self.device_id, self.config)
272
+ elif self.config.tts == "fish":
273
+ return TetosLiveTTS(self.mina_service, self.device_id, self.config)
263
274
  else:
264
- return TetosTTS(self.mina_service, self.device_id, self.config)
275
+ return TetosFileTTS(self.mina_service, self.device_id, self.config)
265
276
 
266
277
  async def wait_for_tts_finish(self):
267
278
  while True:
@@ -331,6 +342,7 @@ class MiGPT:
331
342
  async def stop_if_xiaoai_is_playing(self):
332
343
  is_playing = await self.get_if_xiaoai_is_playing()
333
344
  if is_playing:
345
+ self.log.debug("Muting xiaoai")
334
346
  # stop it
335
347
  await self.mina_service.player_pause(self.device_id)
336
348
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xiaogpt
3
- Version: 3.3
3
+ Version: 3.5
4
4
  Summary: Play ChatGPT or other LLM with xiaomi AI speaker
5
5
  Author-Email: yihong0618 <zouzou0208@gmail.com>
6
6
  License: MIT
@@ -65,6 +65,7 @@ Requires-Dist: h11==0.14.0; extra == "locked"
65
65
  Requires-Dist: httpcore==1.0.5; extra == "locked"
66
66
  Requires-Dist: httplib2==0.22.0; extra == "locked"
67
67
  Requires-Dist: httpx==0.27.2; extra == "locked"
68
+ Requires-Dist: httpx-ws==0.6.2; extra == "locked"
68
69
  Requires-Dist: httpx[socks]==0.27.2; extra == "locked"
69
70
  Requires-Dist: idna==3.7; extra == "locked"
70
71
  Requires-Dist: jiter==0.5.0; extra == "locked"
@@ -111,14 +112,14 @@ Requires-Dist: socksio==1.0.0; extra == "locked"
111
112
  Requires-Dist: soupsieve==2.5; extra == "locked"
112
113
  Requires-Dist: sqlalchemy==2.0.25; extra == "locked"
113
114
  Requires-Dist: tenacity==8.2.3; extra == "locked"
114
- Requires-Dist: tetos==0.3.1; extra == "locked"
115
+ Requires-Dist: tetos==0.4.1; extra == "locked"
115
116
  Requires-Dist: tqdm==4.66.1; extra == "locked"
116
117
  Requires-Dist: typing-extensions==4.12.2; extra == "locked"
117
118
  Requires-Dist: typing-inspect==0.9.0; extra == "locked"
118
119
  Requires-Dist: uritemplate==4.1.1; extra == "locked"
119
120
  Requires-Dist: urllib3==2.1.0; extra == "locked"
120
121
  Requires-Dist: websocket-client==1.8.0; extra == "locked"
121
- Requires-Dist: websockets==12.0; extra == "locked"
122
+ Requires-Dist: wsproto==1.2.0; extra == "locked"
122
123
  Requires-Dist: yarl==1.14.0; extra == "locked"
123
124
  Requires-Dist: zhipuai==2.1.5.20230904; extra == "locked"
124
125
  Description-Content-Type: text/markdown
@@ -1,7 +1,7 @@
1
- xiaogpt-3.3.dist-info/METADATA,sha256=5nJ9Wb_Io16JM6QtUo8sC7Xfz6CoHI01VBQFwAPvOVw,31468
2
- xiaogpt-3.3.dist-info/WHEEL,sha256=pM0IBB6ZwH3nkEPhtcp50KvKNX-07jYtnb1g1m6Z4Co,90
3
- xiaogpt-3.3.dist-info/entry_points.txt,sha256=q4WRS7kS4kQ5kZX57Fq40VrhCi74NZcyRPRX4JP2veo,61
4
- xiaogpt-3.3.dist-info/licenses/LICENSE,sha256=XdClh516MvlnOf9749JZHCxSB7y6_fyXcWmLDz6IkZY,1063
1
+ xiaogpt-3.5.dist-info/METADATA,sha256=fqymHqLmsFnNBE2G332D8ZbdmEFJ0gI3q08OXn6M3j0,31516
2
+ xiaogpt-3.5.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ xiaogpt-3.5.dist-info/entry_points.txt,sha256=q4WRS7kS4kQ5kZX57Fq40VrhCi74NZcyRPRX4JP2veo,61
4
+ xiaogpt-3.5.dist-info/licenses/LICENSE,sha256=XdClh516MvlnOf9749JZHCxSB7y6_fyXcWmLDz6IkZY,1063
5
5
  xiaogpt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  xiaogpt/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
7
7
  xiaogpt/bot/__init__.py,sha256=BDGvj1JuWVw47qfREWGKnSXeiFg6DVJJAz2rHVryqmc,1160
@@ -15,16 +15,17 @@ xiaogpt/bot/llama_bot.py,sha256=HRR_ycuC6DY5MQTKauXEayQ0o_JKk9t-ea3mblscm8E,708
15
15
  xiaogpt/bot/moonshot_bot.py,sha256=PrVRBskZx-U0lH_3RVe89QJa7WKHYqhpft0089pYQz0,822
16
16
  xiaogpt/bot/qwen_bot.py,sha256=rFCOz5uiUsuhePjPozdCecNv5HGiUTNEhHYNw3Exexs,3590
17
17
  xiaogpt/bot/yi_bot.py,sha256=D7JEIh8KPVMvlOLaEVr9ahvyMaJLGToHP_gWU3RoYPc,784
18
- xiaogpt/cli.py,sha256=HXKkW5yg2q8qCruzjMjNTgcjsvs-qoZWPg6mNC_XSB8,5926
19
- xiaogpt/config.py,sha256=mwlJ26_Yv6BriPuWgZaaxotPCr6rO4MahlJXquawJt0,7048
18
+ xiaogpt/cli.py,sha256=iUPQwiZqIpzu1HoinneijBEfVKV7F_f_YMgqPPZM5lA,5932
19
+ xiaogpt/config.py,sha256=GpK3RXJI_B5YBKMxErAh9wUH1FWqVbQppZM4f7B-EoI,7043
20
20
  xiaogpt/langchain/callbacks.py,sha256=yR9AXQt9OHVYBWC47Q1I_BUT4Xg9iM44vnW2vv0BLpE,2616
21
21
  xiaogpt/langchain/chain.py,sha256=z0cqRlL0ElWnf31ByxZBN7AKOT-svXQDt5_NDft_nYc,1495
22
22
  xiaogpt/langchain/examples/email/mail_box.py,sha256=xauqrjE4-G4XPQnokUPE-MZgAaHQ_VrUDLlbfYTdCoo,6372
23
23
  xiaogpt/langchain/examples/email/mail_summary_tools.py,sha256=6cWvBJUaA7iaywcHdbUoww8WiCtaNw3TmwyxyF4DY7E,1561
24
- xiaogpt/tts/__init__.py,sha256=xasHDrmgECirf1MSyrfURSaMBqtdZBi3cQNeDvPo_cQ,145
25
- xiaogpt/tts/base.py,sha256=k0ZUcLJZWU5U_fXu_w-cLFgZpE2KkV89ARbVDXLqTck,4665
24
+ xiaogpt/tts/__init__.py,sha256=75_W5ZhON87RSutiLhJB29Ub-634iI2IlTEZd0alao8,210
25
+ xiaogpt/tts/base.py,sha256=8vP8fIksSZttmrMaUT4vtiDbkfijkr9lbhyod1_5tc4,1440
26
+ xiaogpt/tts/file.py,sha256=pWzozktMAgJraPiANWQC5dB4EQcL9QCwJWbX_M4hqWE,3721
27
+ xiaogpt/tts/live.py,sha256=7-NpuOU7E6GNht1PHjAA8_miXdFVDk8C7aL-sY1xqPE,3299
26
28
  xiaogpt/tts/mi.py,sha256=1MzCB27DBohPQ_4Xz4W_FV9p-chJFDavOHB89NviLcM,1095
27
- xiaogpt/tts/tetos.py,sha256=fkuOSYGqAfJyyPEXbsiOS--CewGf1JUiahoN33nzOAA,1058
28
29
  xiaogpt/utils.py,sha256=YYmRDNtccxqB9gyN_xhKZwgL1_PNYEp7So_-jt2tiVg,2668
29
- xiaogpt/xiaogpt.py,sha256=ZSVsMzwSW0uxwW2VN4phXV1A-suyrwhE0jAMGtaUB5I,16196
30
- xiaogpt-3.3.dist-info/RECORD,,
30
+ xiaogpt/xiaogpt.py,sha256=zHZFijx-tH6vi3roB1uluZUhqLUnhmYNUR367GXVixg,16696
31
+ xiaogpt-3.5.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.2)
2
+ Generator: pdm-backend (2.4.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
xiaogpt/tts/tetos.py DELETED
@@ -1,31 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import tempfile
4
- from pathlib import Path
5
-
6
- from miservice import MiNAService
7
-
8
- from xiaogpt.config import Config
9
- from xiaogpt.tts.base import AudioFileTTS
10
-
11
-
12
- class TetosTTS(AudioFileTTS):
13
- def __init__(
14
- self, mina_service: MiNAService, device_id: str, config: Config
15
- ) -> None:
16
- from tetos import get_speaker
17
-
18
- super().__init__(mina_service, device_id, config)
19
- assert config.tts and config.tts != "mi"
20
- speaker_cls = get_speaker(config.tts)
21
- try:
22
- self.speaker = speaker_cls(**config.tts_options)
23
- except TypeError as e:
24
- raise ValueError(f"{e}. Please add them via `tts_options` config") from e
25
-
26
- async def make_audio_file(self, lang: str, text: str) -> tuple[Path, float]:
27
- output_file = tempfile.NamedTemporaryFile(
28
- suffix=".mp3", mode="wb", delete=False, dir=self.dirname.name
29
- )
30
- duration = await self.speaker.synthesize(text, output_file.name, lang=lang)
31
- return Path(output_file.name), duration