diff --git a/main/manager-api/src/main/resources/db/changelog/202505151451.sql b/main/manager-api/src/main/resources/db/changelog/202505151451.sql new file mode 100644 index 00000000..601de457 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505151451.sql @@ -0,0 +1,18 @@ +-- 修改自定义TTS接口请求定义 +update `ai_model_provider` set `fields` = +'[{"key":"url","label":"服务地址","type":"string"},{"key":"method","label":"请求方式","type":"string"},{"key":"params","label":"请求参数","type":"dict","dict_name":"params"},{"key":"headers","label":"请求头","type":"dict","dict_name":"headers"},{"key":"format","label":"音频格式","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]' +where `id` = 'SYSTEM_TTS_custom'; + +-- 修改自定义TTS配置说明 +UPDATE `ai_model_config` SET +`doc_link` = NULL, +`remark` = '自定义TTS配置说明: +1. 自定义的TTS接口服务,请求参数可自定义,可接入众多TTS服务 +2. 以本地部署的KokoroTTS为例 +3. 如果只有cpu运行:docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:latest +4. 如果只有gpu运行:docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:latest +配置说明: +1. 在params中配置请求参数,使用JSON格式 + 例如KokoroTTS:{ "input": "{prompt_text}", "speed": 1, "voice": "zm_yunxi", "stream": true, "download_format": "mp3", "response_format": "mp3", "return_download_link": true } +2. 在headers中配置请求头 +3. 设置返回音频格式' WHERE `id` = 'TTS_CustomTTS'; \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 22cef133..9d3808da 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -155,4 +155,11 @@ databaseChangeLog: changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202505201744.sql \ No newline at end of file + path: classpath:db/changelog/202505201744.sql + - changeSet: + id: 202505151451 + author: hsoftxl + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202505151451.sql \ No newline at end of file diff --git a/main/manager-web/src/components/DeviceItem.vue b/main/manager-web/src/components/DeviceItem.vue index 0ecbcefa..eeab90a1 100644 --- a/main/manager-web/src/components/DeviceItem.vue +++ b/main/manager-web/src/components/DeviceItem.vue @@ -28,7 +28,7 @@
+ :style="{ filter: $route.path === '/dict-management' || $route.path === '/params-management' || $route.path === '/provider-management' || $route.path === '/server-side-management' ? 'brightness(0) invert(1)' : 'None' }" />
参数字典
diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml
index 05756cd7..44845df8 100644
--- a/main/xiaozhi-server/config.yaml
+++ b/main/xiaozhi-server/config.yaml
@@ -675,17 +675,24 @@ TTS:
speed: 1
output_dir: tmp/
CustomTTS:
- # 自定义的TTS接口服务,请求参数可自定义
- # 要求接口使用GET方式请求,并返回音频文件
+ # 自定义的TTS接口服务,请求参数可自定义,可接入众多TTS服务
+ # 以本地部署的KokoroTTS为例
+ # 如果只有cpu运行:docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:latest
+ # 如果只有gpu运行:docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:latest
+ # 要求接口使用POST方式请求,并返回音频文件
type: custom
- url: "http://127.0.0.1:9880/tts"
+ method: POST
+ url: "http://127.0.0.1:8880/v1/audio/speech"
params: # 自定义请求参数
- # text: "{prompt_text}" # {prompt_text}会被替换为实际的提示词内容
- # speaker: jok老师
- # speed: 1
- # foo: bar
- # testabc: 123456
+ input: "{prompt_text}"
+ response_format: "mp3"
+ download_format: "mp3"
+ voice: "zf_xiaoxiao"
+ lang_code: "z"
+ return_download_link: true
+ speed: 1
+ stream: false
headers: # 自定义请求头
# Authorization: Bearer xxxx
- format: wav # 接口返回的音频格式
+ format: mp3 # 接口返回的音频格式
output_dir: tmp/
\ No newline at end of file
diff --git a/main/xiaozhi-server/core/providers/tts/custom.py b/main/xiaozhi-server/core/providers/tts/custom.py
index 5b8669da..a5300a3e 100644
--- a/main/xiaozhi-server/core/providers/tts/custom.py
+++ b/main/xiaozhi-server/core/providers/tts/custom.py
@@ -1,4 +1,5 @@
import os
+import json
import uuid
import requests
from config.logger import setup_logging
@@ -12,11 +13,21 @@ class TTSProvider(TTSProviderBase):
def __init__(self, config, delete_audio_file):
super().__init__(config, delete_audio_file)
self.url = config.get("url")
+ self.method = config.get("method", "GET")
self.headers = config.get("headers", {})
- self.params = config.get("params")
self.format = config.get("format", "wav")
self.output_file = config.get("output_dir", "tmp/")
+ self.params = config.get("params")
+
+ if isinstance(self.params, str):
+ try:
+ self.params = json.loads(self.params)
+ except json.JSONDecodeError:
+ raise ValueError("Custom TTS配置参数出错,无法将字符串解析为对象")
+ elif not isinstance(self.params, dict):
+ raise TypeError("Custom TTS配置参数出错, 请参考配置说明")
+
def generate_filename(self):
return os.path.join(self.output_file, f"tts-{datetime.now().date()}@{uuid.uuid4().hex}.{self.format}")
@@ -27,7 +38,10 @@ class TTSProvider(TTSProviderBase):
v = v.replace("{prompt_text}", text)
request_params[k] = v
- resp = requests.get(self.url, params=request_params, headers=self.headers)
+ if self.method.upper() == "POST":
+ resp = requests.post(self.url, json=request_params, headers=self.headers)
+ else:
+ resp = requests.get(self.url, params=request_params, headers=self.headers)
if resp.status_code == 200:
with open(output_file, "wb") as file:
file.write(resp.content)