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 +3 -2
- xiaogpt/config.py +1 -1
- xiaogpt/tts/__init__.py +3 -2
- xiaogpt/tts/base.py +1 -90
- xiaogpt/tts/file.py +103 -0
- xiaogpt/tts/live.py +103 -0
- xiaogpt/xiaogpt.py +21 -9
- {xiaogpt-3.3.dist-info → xiaogpt-3.5.dist-info}/METADATA +4 -3
- {xiaogpt-3.3.dist-info → xiaogpt-3.5.dist-info}/RECORD +12 -11
- {xiaogpt-3.3.dist-info → xiaogpt-3.5.dist-info}/WHEEL +1 -1
- xiaogpt/tts/tetos.py +0 -31
- {xiaogpt-3.3.dist-info → xiaogpt-3.5.dist-info}/entry_points.txt +0 -0
- {xiaogpt-3.3.dist-info → xiaogpt-3.5.dist-info}/licenses/LICENSE +0 -0
xiaogpt/cli.py
CHANGED
xiaogpt/config.py
CHANGED
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", "
|
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,
|
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
|
-
|
61
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
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
|
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
|
+
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.
|
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:
|
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.
|
2
|
-
xiaogpt-3.
|
3
|
-
xiaogpt-3.
|
4
|
-
xiaogpt-3.
|
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=
|
19
|
-
xiaogpt/config.py,sha256=
|
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=
|
25
|
-
xiaogpt/tts/base.py,sha256=
|
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=
|
30
|
-
xiaogpt-3.
|
30
|
+
xiaogpt/xiaogpt.py,sha256=zHZFijx-tH6vi3roB1uluZUhqLUnhmYNUR367GXVixg,16696
|
31
|
+
xiaogpt-3.5.dist-info/RECORD,,
|
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
|
File without changes
|
File without changes
|