diff --git a/docs/Deployment.md b/docs/Deployment.md index 0bbe629d..097aea59 100644 --- a/docs/Deployment.md +++ b/docs/Deployment.md @@ -137,7 +137,10 @@ docker logs -f xiaozhi-esp32-server ``` docker stop xiaozhi-esp32-server docker rm xiaozhi-esp32-server +docker stop xiaozhi-esp32-server-web +docker rm xiaozhi-esp32-server-web docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest +docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest ``` 5.3、重新按docker方式部署 @@ -221,7 +224,7 @@ python app.py ## 配置项目 如果你的`xiaozhi-server`目录没有`data`,你需要创建`data`目录。 -如果你的`data`下面没有`.config.yaml`文件,你可以把源码目录下的`config.yaml`文件复制一份,重命名为`.config.yaml` +如果你的`data`下面没有`.config.yaml`文件,你可以把`xiaozhi-server`目录下的`config.yaml`文件复制到`data`,并重命名为`.config.yaml` 修改`xiaozhi-server`下`data`目录下的`.config.yaml`文件,配置本项目必须的一个配置。 diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index 24a2eb8b..d48a1796 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -198,7 +198,11 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/ 5.1、执行以下命令 ``` -docker compose down +docker-compose down +docker stop xiaozhi-esp32-server +docker rm xiaozhi-esp32-server +docker stop xiaozhi-esp32-server-web +docker rm xiaozhi-esp32-server-web docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest docker compose pull @@ -373,6 +377,9 @@ pip install -r requirements.txt `server.secret`需要说明一下,这个`参数值`很重要,作用是让我们的`Server`端连接`manager-api`。`server.secret`是每次从零部署manager模块时,会自动随机生成的密钥。 +如果你的`xiaozhi-server`目录没有`data`,你需要创建`data`目录。 +如果你的`data`下面没有`.config.yaml`文件,你可以把`xiaozhi-server`目录下的`config_from_api.yaml`文件复制到`data`,并重命名为`.config.yaml` + 复制`参数值`后,打开`xiaozhi-server`下的`data`目录的`.config.yaml`文件。此刻你的配置文件内容应该是这样的: ``` diff --git a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java index 78a7b511..7be38a89 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java @@ -171,7 +171,13 @@ public class ConfigServiceImpl implements ConfigService { switch (param.getValueType().toLowerCase()) { case "number": try { - current.put(lastKey, Double.parseDouble(value)); + double doubleValue = Double.parseDouble(value); + // 如果数值是整数形式,则转换为Integer + if (doubleValue == (int) doubleValue) { + current.put(lastKey, (int) doubleValue); + } else { + current.put(lastKey, doubleValue); + } } catch (NumberFormatException e) { current.put(lastKey, value); } diff --git a/main/manager-web/.env.development b/main/manager-web/.env.development index ced1a9e7..0a1de306 100644 --- a/main/manager-web/.env.development +++ b/main/manager-web/.env.development @@ -1,4 +1 @@ -# 暂时使用群主的接口 -# VUE_APP_API_BASE_URL=https://2662r3426b.vicp.fun/xiaozhi -# 如果本地开发,请使用以下接口 -VUE_APP_API_BASE_URL=http://localhost:8002/xiaozhi \ No newline at end of file +VUE_APP_API_BASE_URL=/xiaozhi \ No newline at end of file diff --git a/main/manager-web/vue.config.js b/main/manager-web/vue.config.js index e29c0de7..5df17711 100644 --- a/main/manager-web/vue.config.js +++ b/main/manager-web/vue.config.js @@ -15,6 +15,10 @@ module.exports = defineConfig({ devServer: { port: 8001, // 指定端口为 8001 proxy: { + '/xiaozhi': { + target: 'http://127.0.0.1:8002', + changeOrigin: true + } }, client: { overlay: false, // 不显示 webpack 错误覆盖层 diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index fc191b6f..3a69d096 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -204,7 +204,7 @@ class ConnectionHandler: self.headers.get("device-id", None), self.headers.get("client-id", None), ) - private_config["delete_audio"] = self.config["delete_audio"] + private_config["delete_audio"] = bool(self.config["delete_audio"], True) self.logger.bind(tag=TAG).info(f"获取差异化配置成功: {private_config}") except Exception as e: self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}") @@ -702,7 +702,7 @@ class ConnectionHandler: opus_datas, text_index, tts_file = [], 0, None try: self.logger.bind(tag=TAG).debug("正在处理TTS任务...") - tts_timeout = self.config.get("tts_timeout", 10) + tts_timeout = int(self.config.get("tts_timeout", 10)) tts_file, text, text_index = future.result(timeout=tts_timeout) if text is None or len(text) <= 0: self.logger.bind(tag=TAG).error( diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index f798739a..c0ffa429 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -71,8 +71,8 @@ async def no_voice_close_connect(conn): conn.client_no_voice_last_time = time.time() * 1000 else: no_voice_time = time.time() * 1000 - conn.client_no_voice_last_time - close_connection_no_voice_time = conn.config.get( - "close_connection_no_voice_time", 120 + close_connection_no_voice_time = int( + conn.config.get("close_connection_no_voice_time", 120) ) if ( not conn.close_after_chat diff --git a/main/xiaozhi-server/core/providers/tts/fishspeech.py b/main/xiaozhi-server/core/providers/tts/fishspeech.py index 6d59243b..6456961d 100644 --- a/main/xiaozhi-server/core/providers/tts/fishspeech.py +++ b/main/xiaozhi-server/core/providers/tts/fishspeech.py @@ -89,19 +89,19 @@ class TTSProvider(TTSProviderBase): self.reference_audio = config.get("reference_audio", []) self.reference_text = config.get("reference_text", []) self.format = config.get("format", "wav") - self.channels = config.get("channels", 1) - self.rate = config.get("rate", 44100) + self.channels = int(config.get("channels", 1)) + self.rate = int(config.get("rate", 44100)) self.api_key = config.get("api_key", "YOUR_API_KEY") have_key = check_model_key("FishSpeech TTS", self.api_key) if not have_key: return self.normalize = config.get("normalize", True) - self.max_new_tokens = config.get("max_new_tokens", 1024) - self.chunk_length = config.get("chunk_length", 200) - self.top_p = config.get("top_p", 0.7) - self.repetition_penalty = config.get("repetition_penalty", 1.2) - self.temperature = config.get("temperature", 0.7) - self.streaming = config.get("streaming", False) + self.max_new_tokens = int(config.get("max_new_tokens", 1024)) + self.chunk_length = int(config.get("chunk_length", 200)) + self.top_p = float(config.get("top_p", 0.7)) + self.repetition_penalty = float(config.get("repetition_penalty", 1.2)) + self.temperature = float(config.get("temperature", 0.7)) + self.streaming = bool(config.get("streaming", False)) self.use_memory_cache = config.get("use_memory_cache", "on") self.seed = config.get("seed") self.api_url = config.get("api_url", "http://127.0.0.1:8080/v1/tts") diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py index ae20beb6..311ef5b0 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py @@ -10,6 +10,7 @@ from core.providers.tts.base import TTSProviderBase TAG = __name__ logger = setup_logging() + class TTSProvider(TTSProviderBase): def __init__(self, config, delete_audio_file): super().__init__(config, delete_audio_file) @@ -18,23 +19,26 @@ class TTSProvider(TTSProviderBase): self.ref_audio_path = config.get("ref_audio_path") self.prompt_text = config.get("prompt_text") self.prompt_lang = config.get("prompt_lang", "zh") - self.top_k = config.get("top_k", 5) - self.top_p = config.get("top_p", 1) - self.temperature = config.get("temperature", 1) + self.top_k = int(config.get("top_k", 5)) + self.top_p = float(config.get("top_p", 1)) + self.temperature = float(config.get("temperature", 1)) self.text_split_method = config.get("text_split_method", "cut0") - self.batch_size = config.get("batch_size", 1) - self.batch_threshold = config.get("batch_threshold", 0.75) - self.split_bucket = config.get("split_bucket", True) - self.return_fragment = config.get("return_fragment", False) - self.speed_factor = config.get("speed_factor", 1.0) - self.streaming_mode = config.get("streaming_mode", False) - self.seed = config.get("seed", -1) - self.parallel_infer = config.get("parallel_infer", True) - self.repetition_penalty = config.get("repetition_penalty", 1.35) + self.batch_size = int(config.get("batch_size", 1)) + self.batch_threshold = float(config.get("batch_threshold", 0.75)) + self.split_bucket = bool(config.get("split_bucket", True)) + self.return_fragment = bool(config.get("return_fragment", False)) + self.speed_factor = float(config.get("speed_factor", 1.0)) + self.streaming_mode = bool(config.get("streaming_mode", False)) + self.seed = int(config.get("seed", -1)) + self.parallel_infer = bool(config.get("parallel_infer", True)) + self.repetition_penalty = float(config.get("repetition_penalty", 1.35)) self.aux_ref_audio_paths = config.get("aux_ref_audio_paths", []) def generate_filename(self, extension=".wav"): - return os.path.join(self.output_file, f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + return os.path.join( + self.output_file, + f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}", + ) async def text_to_speak(self, text, output_file): request_json = { @@ -56,7 +60,7 @@ class TTSProvider(TTSProviderBase): "streaming_mode": self.streaming_mode, "seed": self.seed, "parallel_infer": self.parallel_infer, - "repetition_penalty": self.repetition_penalty + "repetition_penalty": self.repetition_penalty, } resp = requests.post(self.url, json=request_json) @@ -64,4 +68,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error(f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}") + logger.bind(tag=TAG).error( + f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}" + ) diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py index ebfb12f1..25c52b84 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py @@ -8,6 +8,7 @@ from core.providers.tts.base import TTSProviderBase TAG = __name__ logger = setup_logging() + class TTSProvider(TTSProviderBase): def __init__(self, config, delete_audio_file): super().__init__(config, delete_audio_file) @@ -16,18 +17,20 @@ class TTSProvider(TTSProviderBase): self.prompt_text = config.get("prompt_text") self.prompt_language = config.get("prompt_language") self.text_language = config.get("text_language", "audo") - self.top_k = config.get("top_k", 15) - self.top_p = config.get("top_p", 1.0) - self.temperature = config.get("temperature", 1.0) - self.cut_punc = config.get("cut_punc","") - self.speed = config.get("speed", 1.0) - self.inp_refs = config.get("inp_refs",[]) - self.sample_steps = config.get("sample_steps",32) - self.if_sr = config.get("if_sr",False) - + self.top_k = int(config.get("top_k", 15)) + self.top_p = float(config.get("top_p", 1.0)) + self.temperature = float(config.get("temperature", 1.0)) + self.cut_punc = config.get("cut_punc", "") + self.speed = float(config.get("speed", 1.0)) + self.inp_refs = config.get("inp_refs", []) + self.sample_steps = int(config.get("sample_steps", 32)) + self.if_sr = bool(config.get("if_sr", False)) def generate_filename(self, extension=".wav"): - return os.path.join(self.output_file, f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + return os.path.join( + self.output_file, + f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}", + ) async def text_to_speak(self, text, output_file): request_params = { @@ -51,4 +54,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error(f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}") + logger.bind(tag=TAG).error( + f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}" + ) diff --git a/main/xiaozhi-server/core/providers/tts/openai.py b/main/xiaozhi-server/core/providers/tts/openai.py index 18daf29b..a5d48057 100644 --- a/main/xiaozhi-server/core/providers/tts/openai.py +++ b/main/xiaozhi-server/core/providers/tts/openai.py @@ -21,7 +21,7 @@ class TTSProvider(TTSProviderBase): else: self.voice = config.get("voice", "alloy") self.response_format = "wav" - self.speed = config.get("speed", 1.0) + self.speed = float(config.get("speed", 1.0)) self.output_file = config.get("output_dir", "tmp/") check_model_key("TTS", self.api_key) diff --git a/main/xiaozhi-server/core/providers/tts/siliconflow.py b/main/xiaozhi-server/core/providers/tts/siliconflow.py index 76d131bb..be6b9e2a 100644 --- a/main/xiaozhi-server/core/providers/tts/siliconflow.py +++ b/main/xiaozhi-server/core/providers/tts/siliconflow.py @@ -16,7 +16,7 @@ class TTSProvider(TTSProviderBase): self.voice = config.get("voice") self.response_format = config.get("response_format") self.sample_rate = config.get("sample_rate") - self.speed = config.get("speed") + self.speed = float(config.get("speed")) self.gain = config.get("gain") self.host = "api.siliconflow.cn" diff --git a/main/xiaozhi-server/core/providers/tts/tencent.py b/main/xiaozhi-server/core/providers/tts/tencent.py index c05261fd..f2501e2f 100644 --- a/main/xiaozhi-server/core/providers/tts/tencent.py +++ b/main/xiaozhi-server/core/providers/tts/tencent.py @@ -19,7 +19,7 @@ class TTSProvider(TTSProviderBase): if config.get("private_voice"): self.voice = config.get("private_voice") else: - self.voice = config.get("voice") + self.voice = int(config.get("voice")) self.api_url = "https://tts.tencentcloudapi.com" # 正确的API端点 self.region = config.get("region") self.output_file = config.get("output_dir") diff --git a/main/xiaozhi-server/core/providers/tts/ttson.py b/main/xiaozhi-server/core/providers/tts/ttson.py index ba0a1222..a6401141 100644 --- a/main/xiaozhi-server/core/providers/tts/ttson.py +++ b/main/xiaozhi-server/core/providers/tts/ttson.py @@ -20,13 +20,13 @@ class TTSProvider(TTSProviderBase): self.voice_id = int(config.get("voice_id", 1695)) self.token = config.get("token") self.to_lang = config.get("to_lang") - self.volume_change_dB = config.get("volume_change_dB", 0) - self.speed_factor = config.get("speed_factor", 1) - self.stream = config.get("stream", False) + self.volume_change_dB = int(config.get("volume_change_dB", 0)) + self.speed_factor = int(config.get("speed_factor", 1)) + self.stream = bool(config.get("stream", False)) self.output_file = config.get("output_dir") - self.pitch_factor = config.get("pitch_factor", 0) + self.pitch_factor = int(config.get("pitch_factor", 0)) self.format = config.get("format", "mp3") - self.emotion = config.get("emotion", 1) + self.emotion = int(config.get("emotion", 1)) self.header = {"Content-Type": "application/json"} def generate_filename(self, extension=".mp3"): diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index df05cd6d..3332d9ac 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -21,8 +21,8 @@ class VADProvider(VADProviderBase): (get_speech_timestamps, _, _, _, _) = self.utils self.decoder = opuslib_next.Decoder(16000, 1) - self.vad_threshold = config.get("threshold") - self.silence_threshold_ms = config.get("min_silence_duration_ms") + self.vad_threshold = float(config.get("threshold", 0.5)) + self.silence_threshold_ms = int(config.get("min_silence_duration_ms", 1000)) def is_vad(self, conn, opus_packet): try: diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 07a2c985..dd56f5ae 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -230,7 +230,7 @@ def initialize_modules( modules["tts"] = tts.create_instance( tts_type, config["TTS"][config["selected_module"]["TTS"]], - config["delete_audio"], + bool(config.get("delete_audio", True)), ) logger.bind(tag=TAG).info(f"初始化组件: tts成功") @@ -294,7 +294,7 @@ def initialize_modules( modules["asr"] = asr.create_instance( asr_type, config["ASR"][config["selected_module"]["ASR"]], - config["delete_audio"], + bool(config.get("delete_audio", True)), ) logger.bind(tag=TAG).info(f"初始化组件: asr成功")