Compare commits

...
496 Commits
Author SHA1 Message Date
欣南科技andGitHub 11e8736e66 Merge pull request #2219 from xinnan-tech/i18n-update
Bump to v0.8.1
2025-09-12 20:52:52 +08:00
hrz 30ce36b169 Bump to v0.8.1 2025-09-12 20:52:20 +08:00
欣南科技andGitHub 1541128e03 Merge pull request #2218 from xinnan-tech/i18n-update
update:优化英文显示
2025-09-12 18:44:32 +08:00
hrz 9d808ec733 update:优化英文显示 2025-09-12 18:43:40 +08:00
欣南科技andGitHub 8f526820a8 Merge pull request #2217 from xinnan-tech/i18n-update
update:优化英文显示
2025-09-12 18:33:45 +08:00
hrz c0689c6916 update:优化英文显示 2025-09-12 18:33:14 +08:00
欣南科技andGitHub 893872e509 Merge pull request #2216 from xinnan-tech/manager-web-i18n
Manager web i18n
2025-09-12 18:11:15 +08:00
hrz 2c4b92f941 Merge branch 'main' into manager-web-i18n 2025-09-12 18:09:58 +08:00
hrz 21cc1153b0 update:优化显示 2025-09-12 18:09:09 +08:00
CGDandGitHub 3c35380e9d Merge pull request #2215 from xinnan-tech/manager-web-i18n
fix:修复用户管理页面多语言支持的问题
2025-09-12 17:56:06 +08:00
rainv123 2b65cfd369 fix:修复用户管理页面多语言支持的问题 2025-09-12 17:43:22 +08:00
CGDandGitHub 8a44aaa7f9 Merge pull request #2211 from xinnan-tech/manager-web-i18n
updata:智控台页面添加语言切换,支持中英繁
2025-09-12 16:55:53 +08:00
rainv123 4e0bf5542c updata:智控台页面添加语言切换,支持中英繁 2025-09-12 16:14:44 +08:00
rainv123 f788e45bc1 updata:智控台页面添加语言切换,支持中英繁 2025-09-12 15:11:56 +08:00
rainv123 5b4381c257 updata:智控台页面添加语言切换,支持中英繁 2025-09-12 11:41:31 +08:00
rainv123 7396605844 Merge branch 'main' of https://github.com/xinnan-tech/xiaozhi-esp32-server 2025-09-12 11:36:24 +08:00
hrzandGitHub b0308af05d Merge pull request #2210 from xinnan-tech/pertext
update:更新流式ASR首词延迟测试工具
2025-09-11 18:01:32 +08:00
rainv123 98a1483032 updata:智控台页面添加语言切换,支持中英繁。 2025-09-11 17:10:50 +08:00
FAN-yeB 07242dbfc0 update:更新流式ASR首词延迟测试工具 2025-09-11 16:32:07 +08:00
hrzandGitHub a9b0775469 Merge pull request #2195 from myifeng/upgrade-requirements
更新一些常用依赖库
2025-09-11 15:06:17 +08:00
hrzandGitHub 26a49ba34c numpy1.26.4和tourch2.2.2兼容,其他版本与兼容性问题 2025-09-11 14:56:53 +08:00
hrzandGitHub f3431ea977 Merge pull request #2203 from xinnan-tech/py_fix_intentllm
fix:修复LLM意图识别的若干问题
2025-09-11 14:46:10 +08:00
3030332422 f0a7ee1c0c fix:修复LLM意图识别 2025-09-11 14:38:27 +08:00
欣南科技andGitHub ad5f085c1f Merge pull request #2208 from xinnan-tech/update_doc
fix:补充文档链接
2025-09-11 13:50:18 +08:00
hrz e7f5f5da34 fix:补充文档链接 2025-09-11 13:49:42 +08:00
欣南科技andGitHub d10b632875 Merge pull request #2207 from xinnan-tech/update_doc
update:添加mqtt网关架设教程
2025-09-11 13:47:38 +08:00
hrz 8dd39c5a5b update:添加mqtt网关架设教程 2025-09-11 13:46:35 +08:00
hrzandGitHub cdd1345c19 Merge pull request #1759 from xinnan-tech/mqtt
Mqtt gateway function
2025-09-10 22:32:37 +08:00
hrzandGitHub 84067a1f57 Merge branch 'main' into mqtt 2025-09-10 22:30:25 +08:00
hrz 26fd3110b4 update:优化代码 2025-09-10 22:20:43 +08:00
hrz 5bd78cac1a update:优化代码 2025-09-10 21:40:25 +08:00
3030332422 0738c52d14 fix:修复LLM意图识别的若干问题 2025-09-10 18:00:39 +08:00
FAN-yeB c93cb3bb52 Reapply "Merge branch 'mqtt' of https://github.com/xinnan-tech/xiaozhi-esp32-server into mqtt"
This reverts commit eea7689c3d.
2025-09-10 17:51:45 +08:00
FAN-yeB eea7689c3d Revert "Merge branch 'mqtt' of https://github.com/xinnan-tech/xiaozhi-esp32-server into mqtt"
This reverts commit 7b9e34c3e5, reversing
changes made to f81be33539.
2025-09-10 17:48:31 +08:00
FAN-yeB 7b9e34c3e5 Merge branch 'mqtt' of https://github.com/xinnan-tech/xiaozhi-esp32-server into mqtt 2025-09-10 17:47:13 +08:00
FAN-yeB f81be33539 优化mqtt启用 2025-09-10 17:46:55 +08:00
hrzandGitHub 21a1cb65fc Merge pull request #2202 from xinnan-tech/mqtt2
update:优化智控台参数名称
2025-09-10 17:39:24 +08:00
hrz ef02a01439 update:优化智控台参数名称 2025-09-10 17:38:29 +08:00
hrz 0b22f4bbd2 Merge branch 'mqtt2' into mqtt
# Conflicts:
#	main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java
#	main/manager-api/src/main/java/xiaozhi/modules/device/controller/OTAController.java
#	main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceReportRespDTO.java
#	main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java
#	main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
2025-09-10 12:30:52 +08:00
hrzandGitHub 9f8ae62ebb Merge pull request #2197 from ifnofi/tts_aliyun_cosyvoice
添加阿里云CosyVoice 和 修复文件编码
2025-09-10 11:54:00 +08:00
hrzandGitHub cec71a28eb 已有阿里云cosyvoice流式接口
已有阿里云cosyvoice流式接口,位于main/xiaozhi-server/core/providers/tts/aliyun_stream.py
2025-09-10 11:52:09 +08:00
hrzandGitHub 7d060e9994 Merge pull request #2194 from xinnan-tech/Qwen3_asr
update:更新Qwen-asr-flash语音识别
2025-09-10 11:42:51 +08:00
hrzandGitHub a3a6e889ee Merge branch 'main' into Qwen3_asr 2025-09-10 11:42:42 +08:00
ifnofi 80283db9bd 添加阿里云CosyVoice 和 修复文件编码
添加阿里云的CosyVoice功能
更新wakeup_word.py,在读写配置文件时指定UTF-8编码,以确保正确处理非ascii字符。
2025-09-10 11:17:56 +08:00
FAN-yeB 906a322675 更新Qwen3-ASR-Flash模型配置的说明文档 2025-09-10 11:10:34 +08:00
hrzandGitHub bc829d9ef0 Merge pull request #2170 from xinnan-tech/fix
fix:增加检测到手机浏览器跳转h5页面,修改h5页面图标
2025-09-09 22:52:03 +08:00
myifengandGitHub c5e6a46450 Update requirements.txt 2025-09-09 19:35:32 +08:00
FAN-yeB 77f0bdc4ca update:更新Qwen-asr-flash语音识别 2025-09-09 17:56:05 +08:00
CGDandGitHub 8b9f13283b Merge pull request #2186 from xinnan-tech/py_MinmaxStreamTTS_test
Py minmax stream tts test
2025-09-09 13:17:55 +08:00
CGDandGitHub c3c65708e0 Merge branch 'main' into py_MinmaxStreamTTS_test 2025-09-09 13:17:09 +08:00
3030332422 6bc64aa4aa update:删除非流式MiniMax TTS配置,保留流式版本 2025-09-09 11:38:55 +08:00
FAN-yeB eee907b1a2 兼容udp和websocket协议传输音频 2025-09-09 09:37:31 +08:00
hrzandGitHub ddd63f4136 Merge pull request #2190 from xinnan-tech/py_fix_voiceprint
update:声纹识别添加相似度阈值检查机制
2025-09-09 09:15:29 +08:00
CGDandGitHub b12c1efc90 Merge branch 'main' into py_fix_voiceprint 2025-09-08 18:13:51 +08:00
hrzandGitHub 01838f598c Merge pull request #2189 from myifeng/fix-voiceprint-enable
修正声纹识别开启校验
2025-09-08 17:57:07 +08:00
hrzandGitHub aef6d5dc1d 在不增加额外配置的情况下进行优化 2025-09-08 17:55:48 +08:00
hrzandGitHub 9262963f20 在不增加额外配置的情况下进行优化 2025-09-08 17:55:41 +08:00
Sakura-RanChenandGitHub d3e2aaacb9 Merge pull request #2126 from xinnan-tech/py_test
添加vosk
2025-09-08 17:47:04 +08:00
3030332422 755c81286c update:修改vosk 2025-09-08 17:35:18 +08:00
hrzandGitHub 9dd2d998e2 Merge branch 'main' into fix 2025-09-08 16:59:54 +08:00
Chingfeng Li abbb13161e Merge branch 'fix-voiceprint-enable' of https://github.com/myifeng/xiaozhi-esp32-server into fix-voiceprint-enable 2025-09-08 16:27:38 +08:00
Chingfeng Li 23dc6f1ea9 声纹识别功能开关 2025-09-08 16:26:39 +08:00
3030332422 8aed23f21a update:智控台添加相似度阈值配置 2025-09-08 16:25:08 +08:00
hrzandGitHub ae117e3579 Merge pull request #2188 from myifeng/weather-config
优化天气配置
2025-09-08 15:49:37 +08:00
Chingfeng Li 0d19c693a9 优化天气配置 2025-09-08 14:24:50 +08:00
hrzandGitHub 283e57ff71 Merge pull request #2165 from myifeng/sse-mcp-fix
优化SSE Client连接;增加SSE MCP示例
2025-09-08 13:28:25 +08:00
FAN-yeB eed9503391 mqtt 2025-09-08 11:04:42 +08:00
3030332422 d66db60177 update:修改声纹日志 2025-09-08 09:32:10 +08:00
3030332422 25d9e6c8cf update: 声纹识别添加相似度阈值检查机制 2025-09-07 15:30:04 +08:00
Sakura-RanChen a81f433228 update: 增加相关配置 2025-09-05 17:52:30 +08:00
3030332422 257e365299 Merge remote-tracking branch 'origin/main' into py_test 2025-09-05 14:56:08 +08:00
Sakura-RanChen 554afa3f46 Merge branch 'main' into py_MinmaxStreamTTS_test 2025-09-05 14:53:02 +08:00
欣南科技andGitHub d04ec9d510 Merge pull request #2181 from xinnan-tech/update_version
update:升级版本号
2025-09-05 13:59:14 +08:00
hrz 4a802e00e4 update:升级版本号 2025-09-05 13:58:26 +08:00
CGDandGitHub a7007f5ae2 Merge pull request #2178 from xinnan-tech/future
fix: 方法修正
2025-09-05 10:37:21 +08:00
Sakura-RanChen 2b3cacd5b0 fix: 方法修正 2025-09-05 10:31:47 +08:00
CGDandGitHub 49565b4b3c Merge pull request #2158 from xinnan-tech/future
Future
2025-09-05 10:16:39 +08:00
CGDandGitHub bd8ae27f92 Merge branch 'main' into future 2025-09-05 10:14:52 +08:00
rainv123andruoyemuzimi 3fe0b8b669 fix:修改手机浏览器访问web后台跳转至h5页面的逻辑 2025-09-05 10:09:32 +08:00
Sakura-RanChenandGitHub f0e0b97217 Merge pull request #2166 from xinnan-tech/py_test_texthandle
textHandle.py 消息解耦
2025-09-05 09:14:32 +08:00
rainv123andruoyemuzimi 67e07c916e docs:添加火山双向流式TTS+声音克隆配置教程 2025-09-04 16:37:28 +08:00
rainv123andruoyemuzimi b3a6ae2441 fix:增加检测到手机浏览器跳转h5页面,修改h5页面图标 2025-09-04 09:32:53 +08:00
Chingfeng Li 74f321a0fd 补充兼容方法和过期警告 2025-09-03 17:53:02 +08:00
hrzandGitHub 64c841d00a Merge pull request #2156 from xinnan-tech/py_fix_FishTTS
fix: 变量名冲突
2025-09-03 17:49:23 +08:00
Sakura-RanChen 5afa2fdc98 fix: 降低误判 2025-09-03 17:29:21 +08:00
Chingfeng Li 74bc5dbb7d 优化SSE Client连接;增加SSE MCP示例 2025-09-03 15:41:35 +08:00
CGDandGitHub 5c67f2ecfd Merge pull request #2162 from myifeng/main
textHandle.py 消息解耦
2025-09-03 14:02:40 +08:00
Sakura-RanChen 66f382039c fix: 打断时状态未回正,提示无法正确播报 2025-09-03 10:46:24 +08:00
Chingfeng Li 3cfc1cf97a textHandle.py 消息解耦 2025-09-03 09:43:34 +08:00
Sakura-RanChen b88b102d38 修改注解 2025-09-02 17:08:56 +08:00
Sakura-RanChen fa3ddc2e2b 去除无用变量 2025-09-02 17:06:19 +08:00
Sakura-RanChen f45bc5d9c9 优化音频 2025-09-02 15:24:51 +08:00
Sakura-RanChen 79956ade97 fix: 变量名冲突 2025-09-02 14:36:13 +08:00
Sakura-RanChen b9df6d4ad2 fix: 优化 2025-09-02 14:34:26 +08:00
hrzandGitHub fa79e2b379 Merge pull request #2153 from xinnan-tech/py_test_if
docs: 添加天气插件使用指南
2025-09-02 14:05:36 +08:00
hrzandGitHub 5db638c075 Update weather-integration.md 2025-09-02 14:04:42 +08:00
3030332422 0f0a0814b7 fix:补充 2025-09-02 12:45:51 +08:00
3030332422 a842f0688f fix:优化天气插件指南 2025-09-02 12:36:04 +08:00
Sakura-RanChen c3c2e7c28e fix: 提示尾音播放和单模块第一次唤醒时等待tts初始化 2025-09-02 12:00:04 +08:00
3030332422 0364d1199b docs: 添加天气插件使用指南 2025-09-02 11:23:46 +08:00
Sakura-RanChen c428a9ddee fix: 唤醒机制 2025-09-02 11:18:04 +08:00
hrzandGitHub dba571727a Merge pull request #2149 from xinnan-tech/py_fix_news
fix:添加user-agent修复新闻403错误
2025-09-01 17:01:02 +08:00
3030332422 d31761e78a fix:添加user-agent修复新闻403错误 2025-09-01 16:08:51 +08:00
欣南科技andGitHub 63dfcde4c8 Merge pull request #2143 from xinnan-tech/update-doc
update:添加编译源
2025-08-31 08:07:23 +08:00
hrz 32e6ecf00d update:添加编译源 2025-08-31 08:06:41 +08:00
欣南科技andGitHub 2481509dc5 Merge pull request #2142 from xinnan-tech/update-doc
update:优化编译
2025-08-31 07:25:53 +08:00
hrz 44dd7fc58c update:优化编译 2025-08-31 07:25:14 +08:00
hrz 87f50e5253 update:更新支持的组件 2025-08-31 02:56:22 +08:00
欣南科技andGitHub bffed8bb8a Merge pull request #2141 from xinnan-tech/fix-tool-input-bg
update:修复mcp input样式污染其他input的bug
2025-08-31 00:00:30 +08:00
hrz ff3c0ab7ca update:修复mcp input样式污染其他input的bug 2025-08-30 23:58:11 +08:00
欣南科技andGitHub 6d0addf005 Merge pull request #2140 from xinnan-tech/rollback
update:流式优化暂不稳定,先回滚到早期代码
2025-08-30 23:03:18 +08:00
hrz 1520874432 update:升级版本号 2025-08-30 23:02:50 +08:00
hrz eaade698fb update:流式优化暂不稳定,先回滚到早期代码 2025-08-30 22:59:21 +08:00
欣南科技andGitHub b4f4995ff9 Merge pull request #2139 from xinnan-tech/py_wakeup_audio
Py wakeup audio
2025-08-30 18:15:40 +08:00
hrzandGitHub 76a7ac04c9 Merge branch 'main' into py_wakeup_audio 2025-08-30 18:15:09 +08:00
欣南科技andGitHub 3e1765a26d Merge pull request #2138 from xinnan-tech/update-doc
update:更换Demo视频连接
2025-08-29 13:48:42 +08:00
hrz 95b80f6bb1 update:更换Demo视频连接 2025-08-29 13:47:40 +08:00
hrzandGitHub caf5f3f04c Merge pull request #2134 from xinnan-tech/py_test_tts
update:AliAppLLM流式接口改造
2025-08-28 19:19:39 +08:00
Sakura-RanChen 41b8fac3aa update: 增加唤醒时声纹处理 ,1秒内发送至大模型 【需优化唤醒锁机制,中途会遭受打断(偶发),考虑忽略检测】 2025-08-28 17:58:23 +08:00
3030332422 f6f240bc4b update:AliAppLLM流式接口改造 2025-08-28 16:32:00 +08:00
欣南科技andGitHub a79d8d61e3 Merge pull request #2128 from xinnan-tech/ha-timeout
update:优化ha插件超时方法
2025-08-28 00:40:19 +08:00
hrz a5e748f775 update:优化ha插件超时方法 2025-08-28 00:39:43 +08:00
hrzandGitHub 0fc4edc431 Merge pull request #2114 from xinnan-tech/paddlespeech
update:更新paddlespeechtts供应器
2025-08-27 23:22:04 +08:00
3030332422 c82cf670de update:删去默认模型配置 2025-08-27 16:42:17 +08:00
3030332422 a3827aed6e fix:修改使用说明 2025-08-27 15:54:25 +08:00
3030332422 cc7ba0872c update:添加vosk 2025-08-27 14:39:05 +08:00
gitjianyuandGitHub 3378d2cd8f Merge pull request #2102 from Minamiyama/refactor/extract-streaming-context
refactor(audio): 将流式音频处理逻辑提取为独立类
2025-08-27 10:33:07 +08:00
hrzandGitHub e004a85f53 Merge pull request #2115 from xinnan-tech/py_fix_mcp
fix:修复“服务端mcp 未添加到 function 列表中”问题
2025-08-27 10:16:59 +08:00
Sakura-RanChen 91cd843cfe update: 恢复唤醒播放机制,待优化和改造(目前只有linkerai拥有正常保存唤醒音频to_tts) 2025-08-27 10:04:27 +08:00
FAN-yeB 880b669877 还原配置文件 2025-08-26 11:49:08 +08:00
FAN-yeB e53ba28c65 更新paddlespeechtts供应器 2025-08-26 11:47:52 +08:00
3030332422 bdb6d3c093 fix:修复“服务端mcp 未添加到 function 列表中” 2025-08-26 11:45:52 +08:00
FAN-yeB 9af77d0f47 更新paddlespeechtts供应器 2025-08-26 11:24:58 +08:00
FAN-yeB 15291bfcf2 更新paddlespeechtts供应器 2025-08-26 11:02:57 +08:00
FAN-yeB ae228f3b86 update:更新paddlespeechtts供应器 2025-08-26 10:33:38 +08:00
欣南科技andGitHub b8803f92e7 Merge pull request #2111 from xinnan-tech/duplicate-model
Duplicate model
2025-08-25 22:28:36 +08:00
hrzandGitHub 1515784700 Merge pull request #2104 from Minamiyama/feat/model-management/duplicate-model
feat(模型管理): 添加模型副本创建功能
2025-08-25 22:23:34 +08:00
hrzandGitHub 06e2909fda Merge pull request #2110 from myifeng/main
移除未使用的变量
2025-08-25 22:15:06 +08:00
hrzandGitHub 9012deeae7 Update connection.py 2025-08-25 22:14:41 +08:00
Chingfeng Li 31657e1dc0 移除未使用的变量 2025-08-25 18:26:55 +08:00
欣南科技andGitHub 5a08a41cc8 Merge pull request #2105 from xinnan-tech/update_doc
update:增加移动端贡献者信息
2025-08-25 09:22:20 +08:00
hrz 3e48d7e331 update:增加移动端贡献者信息 2025-08-25 09:21:48 +08:00
Minamiyama 5993470d3f feat(模型管理): 添加模型副本创建功能
在模型编辑对话框中增加副本创建功能,当选择创建副本时自动在原模型名称和代码后添加'_副本'后缀,并通过新增API接口保存为新模型
2025-08-24 23:40:54 +08:00
Minamiyama 18acec1a81 refactor(audio): 将流式音频处理逻辑提取为独立类
将原本内联在test_page.html中的流式音频处理逻辑重构为独立的StreamingContext类,提高代码可维护性和复用性
2025-08-24 11:31:30 +08:00
欣南科技andGitHub 701936326d Merge pull request #2100 from xinnan-tech/py_fix_audio
update:显示天气接口错误信息
2025-08-23 23:25:52 +08:00
hrz 5b7d613a35 update:显示天气接口错误信息 2025-08-23 23:25:19 +08:00
hrzandGitHub 0ce55ff7a4 Merge pull request #2099 from Minamiyama/refactor/audio-context-singleton
refactor(音频处理): 提取音频上下文创建逻辑到独立函数
2025-08-23 22:25:11 +08:00
欣南科技andGitHub 595edb6f80 Merge pull request #2094 from xinnan-tech/py_fix_audio
fix: 优化流控音频播放 wechat聊天模式错误STT消息发送
2025-08-23 22:23:59 +08:00
Minamiyama d4b75c5d99 refactor(音频处理): 提取音频上下文创建逻辑到独立函数
将重复的音频上下文创建逻辑提取到独立的getAudioContextInstance函数中,减少代码重复并统一音频上下文配置
2025-08-23 07:47:40 +08:00
Sakura-RanChen 1988bced60 fix: 优化流控音频播放 wechat聊天模式错误STT消息发送 2025-08-22 17:10:36 +08:00
hrzandGitHub 60f8dbf418 Merge pull request #2086 from xinnan-tech/ASRPerformanceTester
update:更新流式ASR首词等待时间测试工具
2025-08-21 11:58:31 +08:00
FAN-yeB a708382cfd update:更新流式ASR首词等待时间测试工具 2025-08-21 11:54:06 +08:00
hrzandGitHub 1d95e1420a Merge pull request #2080 from VanillaNahida/main
fix: 修复脚本获取不到ws和视觉分析接口地址的bug,更新README
2025-08-20 22:50:18 +08:00
hrzandGitHub e1bcff82e2 Merge pull request #2084 from xinnan-tech/py_fix_audio
Py fix audio
2025-08-20 16:28:54 +08:00
hrzandGitHub 216f1b701f Merge pull request #2082 from xinnan-tech/fix_mobile
fix:修复mcp地址显示慢,增加服务端地址校验
2025-08-20 16:27:06 +08:00
Sakura-RanChen 0ea18d87a3 fix: asr识别缺字现象,去除无用的变量 2025-08-20 16:22:32 +08:00
hrzandGitHub 6a79d57a93 Update docker-setup.sh 2025-08-20 14:42:46 +08:00
hrzandGitHub 31f596f96c Update docker-setup.sh
全模块部署,OTA地址是在8002端口
2025-08-20 14:41:22 +08:00
hrzandGitHub 7f99895116 Update Deployment_all.md 2025-08-20 14:38:51 +08:00
rainv123 789d756ce1 fix:修复mcp地址显示慢,增加服务端地址校验 2025-08-20 14:36:42 +08:00
hrzandGitHub 570fb29d3c Merge pull request #2081 from xinnan-tech/performance_tester
update:更新流式TTS首词耗时测试工具
2025-08-20 14:36:36 +08:00
FAN-yeB d7dc636d3f update:更新流式TTS测试工具 2025-08-20 14:30:47 +08:00
香草味的纳西妲喵andGitHub 67d3a8f94f Update Deployment_all.md 2025-08-20 14:07:46 +08:00
VanillaNahida 7d7b8ddfb1 fix: 修复脚本获取不到ws和视觉分析接口地址的bug
docs: 更新README说明
2025-08-20 14:00:53 +08:00
Sakura-RanChen 74826c1c59 fix 2025-08-20 09:30:43 +08:00
Sakura-RanChen 0676b87832 update: 增加MinMaxStreamTTS 2025-08-20 09:17:04 +08:00
hrzandGitHub d138e1dcef Merge pull request #2073 from xinnan-tech/web_fix_DeviceManagement
fix:修复设备管理页面全选功能失效问题
2025-08-19 17:24:43 +08:00
3030332422 372dc8ecb0 fix:修复设备管理页面全选功能失效问题 2025-08-19 14:45:14 +08:00
欣南科技andGitHub e8bfe9850e Merge pull request #2071 from xinnan-tech/fix-ota
fix: #2067 #2070 OTA和流控的音频bug
2025-08-19 00:20:46 +08:00
hrz ad50fbe3c9 fix: #2067 #2070 OTA和流控的音频bug 2025-08-19 00:19:41 +08:00
hrzandGitHub 61636dd085 Merge pull request #2068 from rainv123/fix-mobile-server
update:修复切换服务端地址无法生效,登陆界面右上角添加可跳转至设置页的按钮
2025-08-18 16:38:39 +08:00
rainv123andGitHub 5ea669be5a Update 删除无用代码行 2025-08-18 15:56:31 +08:00
rainv123 5260b62287 update:修复切换服务端地址无法生效,登陆界面右上角添加可跳转至设置页的按钮 2025-08-18 14:54:54 +08:00
欣南科技andGitHub 984a84edc1 Merge pull request #2061 from xinnan-tech/llm-tester
update:纠正教程错误描述
2025-08-17 14:15:31 +08:00
hrz 32548878e4 update:纠正教程错误描述 2025-08-17 14:14:25 +08:00
欣南科技andGitHub 093db56c98 Merge pull request #2058 from xinnan-tech/llm-tester
Llm tester
2025-08-17 11:13:47 +08:00
hrz a17f8518d6 update:优化推荐配置 2025-08-17 11:13:04 +08:00
hrz e9e9c5355d update:增加性能测试报告链接 2025-08-17 10:44:13 +08:00
hrz 61ee21d627 update:给LLM性能测试工具增加系统提示词,更符合本项目的实际效果 2025-08-17 09:35:06 +08:00
欣南科技andGitHub a86707f1ff Merge pull request #2056 from xinnan-tech/build-apk
update:回滚取消自动编译apk
2025-08-16 19:58:06 +08:00
hrz be42d80061 update:回滚取消自动编译apk 2025-08-16 19:57:34 +08:00
欣南科技andGitHub 16da6f9696 Merge pull request #2054 from xinnan-tech/dev-ops
update:优化自动部署脚本
2025-08-16 16:36:26 +08:00
hrz 427b0aa197 update:优化自动部署脚本 2025-08-16 16:35:26 +08:00
欣南科技andGitHub a2432d45cc Merge pull request #2052 from xinnan-tech/hot-fix
Hot fix
2025-08-16 15:07:53 +08:00
hrz f62b5d4f22 update:优化常见问题链接 2025-08-16 15:07:13 +08:00
hrz 001225c27b update:优化常见问题 2025-08-16 15:03:02 +08:00
欣南科技andGitHub 3e68f188fa Merge pull request #2051 from xinnan-tech/hot-fix
update:引导测试页面启动
2025-08-16 14:37:04 +08:00
hrz 2d75eb76f1 update:引导测试页面启动 2025-08-16 14:36:23 +08:00
欣南科技andGitHub bcc3ebb6bb Merge pull request #2050 from xinnan-tech/hot-fix
update:增加移动版智控台的说明
2025-08-16 11:39:04 +08:00
hrz 52c4e65686 update:增加移动版智控台的说明 2025-08-16 11:37:23 +08:00
欣南科技andGitHub 44a5afb8a2 Merge pull request #2049 from xinnan-tech/hot-fix
update:优化常见问题,梳理链接教程
2025-08-16 10:34:26 +08:00
hrz 3057d35111 update:优化常见问题,梳理链接教程 2025-08-16 10:33:28 +08:00
欣南科技andGitHub bdf99bc554 Merge pull request #2048 from xinnan-tech/hot-fix
update:优化测试工具目录结构
2025-08-16 10:10:17 +08:00
hrz e70415690c update:优化测试工具目录结构 2025-08-16 10:09:36 +08:00
hrzandGitHub 9b5e4eb9b7 Merge pull request #2039 from xinnan-tech/py_voiceprint_add_health
update:优化声纹识别,添加服务器健康检查机制避免运行时超时等待
2025-08-15 18:27:44 +08:00
hrzandGitHub e2324d3c37 Merge pull request #2044 from xinnan-tech/py_test_audio
Py test audio
2025-08-15 17:59:21 +08:00
hrzandGitHub c395c31e46 Merge pull request #2046 from xinnan-tech/xiaozhi-sercer-testPage
修复测试页面 打断对话多次可能导致无法发出声音的bug
2025-08-15 17:58:48 +08:00
JianYu Zheng 94662e17ba 优化:设置播放最短是0.3秒 2025-08-15 17:33:02 +08:00
3030332422 0bfa5cf723 update:feat: 为声纹识别添加缓存机制 2025-08-15 16:58:56 +08:00
Sakura-RanChen d2f29f335e update: 音频流式优化 2025-08-15 16:33:46 +08:00
JianYu Zheng 4423cb85c2 优化:优化播放逻辑去除多个方法共享属性导致的逻辑错误 2025-08-15 11:26:14 +08:00
CGDandGitHub 46fb997b52 Merge pull request #2035 from xinnan-tech/performance_text
update:更新各组件性能测试功能
2025-08-14 17:58:37 +08:00
3030332422 dcf8c134a2 update:优化声纹识别,添加服务器健康检查机制避免运行时超时等待 2025-08-14 17:57:46 +08:00
JianYu Zheng 7c0df908e9 优化:添加阻塞队列,优化缓存禁止 2025-08-14 14:44:52 +08:00
FAN-yeB 7bb76d7ced 更新 performance_test_tool.py 2025-08-14 10:39:53 +08:00
FAN-yeB 1cfb8de3a0 up:修改性能测试工具读取配置的目录 2025-08-14 10:31:31 +08:00
FAN-yeB 768d2b82d6 update:更新性能测试工具 2025-08-14 10:01:43 +08:00
rainv123andGitHub 7a4431d80c Merge pull request #2031 from xinnan-tech/update-paddlespeech
Update: paddlespeech配置
2025-08-13 16:02:44 +08:00
FAN-yeB 7e63e1d607 update:更新paddlespeech.py 2025-08-13 16:00:33 +08:00
FAN-yeB bda6c1e3ed update:paddlespeech配置 2025-08-13 15:59:48 +08:00
JianYu Zheng a9d116df2f 优化:拆分test_page.html一部分代码 2025-08-13 14:25:04 +08:00
欣南科技andGitHub e7c6c32d40 Merge pull request #2025 from xinnan-tech/openrz-patch-1
Update build-apk.yml
2025-08-12 13:55:04 +08:00
hrzandGitHub cdfe24fcdb Update build-apk.yml 2025-08-12 13:54:47 +08:00
欣南科技andGitHub a9199171df Merge pull request #2024 from xinnan-tech/openrz-patch-1
update:修改版本号
2025-08-12 13:41:17 +08:00
hrz 76b9542507 update:修改版本号 2025-08-12 13:40:40 +08:00
欣南科技andGitHub d30b6a8dcf Merge pull request #2023 from xinnan-tech/openrz-patch-1
Update build-apk.yml
2025-08-12 13:35:45 +08:00
hrzandGitHub 76b5ad1c99 Update build-apk.yml 2025-08-12 13:35:26 +08:00
欣南科技andGitHub 7840e83ada Merge pull request #2022 from xinnan-tech/build-apk
update:优化编译
2025-08-12 13:33:17 +08:00
hrz c4ac9da3e5 update:优化编译 2025-08-12 13:32:27 +08:00
Sakura-RanChenandGitHub 311e5d5cfd Merge pull request #1988 from myifeng/async-handler
调整OPUS函数式处理,不再以数组方式收集再遍历
2025-08-12 10:14:43 +08:00
JianYu Zheng a1f6795d44 优化:删除test_page.html无用的代码 2025-08-12 10:06:41 +08:00
欣南科技andGitHub f8a30ce261 Merge pull request #2021 from xinnan-tech/build-apk
update:测试自动编译apk
2025-08-12 08:52:41 +08:00
hrz ccfd15bda0 update:测试自动编译apk 2025-08-12 08:50:31 +08:00
hrzandGitHub a9610e6ee7 Merge pull request #2016 from xinnan-tech/py_huoshanTTS_AddConfig
update: huoshanTTS add config
2025-08-11 21:46:18 +08:00
hrz 733b36b869 update:补充参数说明 2025-08-11 21:45:32 +08:00
欣南科技andGitHub 70c3f9f929 Merge pull request #2020 from xinnan-tech/hot-fix
fix: #2014  Invalid bound statement (not found)
2025-08-11 21:27:07 +08:00
hrz 210bda15b8 update:更新版本号 2025-08-11 21:24:43 +08:00
hrz 93be1b3443 fix: #2014 Invalid bound statement (not found) 2025-08-11 21:23:00 +08:00
欣南科技andGitHub 533a001aeb Merge pull request #2018 from xinnan-tech/feature/mobile-app
Feature/mobile app
2025-08-11 20:49:04 +08:00
hrz 0b57d78732 update:增加h5的启动方法 2025-08-11 20:47:39 +08:00
hrzandGitHub dce9d1a1e2 Merge pull request #2015 from huangjunsen0406/feature/mobile-app
fix: 修复移动端app json文件丢失
2025-08-11 20:39:35 +08:00
JianYu Zheng 1cff39cec4 优化:拆分test_page.html,把一部分opus和document操作的代码提取出来,减少单个文件大小 2025-08-11 18:04:01 +08:00
JianYu Zheng c7392dcf5a 优化:拆分test_page.html,把一部分document操作的代码提取出来,减少单个文件大小,且把logger.js和xiaoZhiConnect.js也提取出来 2025-08-11 17:44:01 +08:00
Sakura-RanChen 3eb39ebef2 update: huoshanTTS add config 2025-08-11 17:38:17 +08:00
Junsen 041d8d5643 feat: 更新README文档,删除不再使用的请求配置文件,添加TypeScript配置文件,优化设置页面逻辑 2025-08-11 17:26:52 +08:00
JianYu Zheng 1c19046442 优化:拆分test_page.html,把一部分小智连接的代码提取出来,减少单个文件大小 2025-08-11 17:14:00 +08:00
Junsen 3d696aaa64 chore: 更新.gitignore以允许跟踪manager-mobile中的env和json文件;修改LICENSE版权信息;添加环境配置文件;新增manifest.json和pages.json以支持移动应用结构 2025-08-11 16:39:53 +08:00
JianYu Zheng 76516336af 优化:拆分test_page.html,把日志函数提取出来,减少单个文件大小 2025-08-11 16:20:46 +08:00
Junsen fc18677409 chore(gitignore): 允许跟踪 package.json 文件 2025-08-11 16:09:13 +08:00
Junsen 029d3730a8 chore(gitignore): allow tracking of package.json; add manager-mobile/package.json 2025-08-11 16:06:54 +08:00
hrz 8509e62114 update:调整文件夹 2025-08-11 15:48:28 +08:00
hrzandGitHub 6e8f9ca22e Merge pull request #2005 from huangjunsen0406/feature/mobile-app
feat: add complete mobile app project initial commit
2025-08-11 15:42:08 +08:00
Junsen dbc1a515d2 feat: 添加自定义标签组件,更新聊天记录和设备管理页面,优化请求配置 2025-08-11 15:14:23 +08:00
hrzandJunsen 70221e7f62 fix:保存记忆时出错 2025-08-11 15:14:23 +08:00
Sakura-RanChenandJunsen 28f921c38d update: 文档示例补充 2025-08-11 15:14:23 +08:00
hrzandJunsen 30a773601f 升级版本 2025-08-11 15:14:23 +08:00
Sakura-RanChenandJunsen 6cddd5c704 update: 补充安装文档 2025-08-11 15:14:23 +08:00
JianYu Zheng 709bc12b1c 优化:拆分test_page.html,css提取出来,减少单个文件大小 2025-08-11 14:24:06 +08:00
欣南科技andGitHub 171892a894 Merge pull request #2012 from xinnan-tech/py_fix_memory
fix:保存记忆时出错
2025-08-11 14:16:12 +08:00
hrz 535ab315d9 fix:保存记忆时出错 2025-08-11 14:15:34 +08:00
hrzandGitHub de28247b7e Merge pull request #2009 from xinnan-tech/py_IndexStreamTTS
update: 文档示例补充
2025-08-10 17:23:46 +08:00
Sakura-RanChen 6186eb753a update: 文档示例补充 2025-08-09 14:59:02 +08:00
欣南科技andGitHub a18bc85a9b Merge pull request #2008 from xinnan-tech/openrz-patch-1
升级版本
2025-08-09 11:44:03 +08:00
hrzandGitHub 806e4c398d 升级版本 2025-08-09 11:43:33 +08:00
hrzandGitHub a4f1abfa42 Merge pull request #2007 from xinnan-tech/py_IndexStreamTTS
update: 补充安装文档
2025-08-08 22:44:35 +08:00
Sakura-RanChen f2efd9718e update: 补充安装文档 2025-08-08 18:09:56 +08:00
Junsen 031c56e30b feat: 将配置中的名称更改为动态获取的环境变量VITE_APP_TITLE 2025-08-08 17:35:35 +08:00
Junsen 994715d9eb style: 优化移动端页面样式,调整组件布局,增强用户体验 2025-08-08 17:35:35 +08:00
Junsen 90f7af7afa feat: 初始化移动端项目 2025-08-08 17:35:35 +08:00
欣南科技andGitHub 2f3c466833 Merge pull request #2004 from xinnan-tech/py_IndexStreamTTS
Py index stream tts
2025-08-08 17:33:14 +08:00
hrz d742bca205 update:添加indextts 流式语音合成 2025-08-08 17:32:41 +08:00
Sakura-RanChen 8e51d52155 update: 增加单模块IndexTTS 2025-08-08 16:55:17 +08:00
hrzandGitHub a1c4f72470 Merge pull request #2002 from xinnan-tech/manager-api-springdoc
修复api接口文档页面,接口文档和点击按钮的说明不一致,串标题的bug
2025-08-08 14:46:30 +08:00
欣南科技andGitHub 2c11bba25e Merge pull request #2003 from xinnan-tech/update-doc
update:添加新场景
2025-08-08 13:08:26 +08:00
hrz 42a5b4fae7 update:添加新场景 2025-08-08 13:07:21 +08:00
JianYu Zheng a61aedc906 修复api接口文档页面,接口文档和点击按钮的说明不一致,串标题的bug
--pom.xml
1.升级了springdoc 2.8.8, knife4j4.6 与 doc2.7.7版本存在兼容问题,按照官方建议升级到2.8.8 解决了此bug
2.升级了 commons-lang3 3.18.0,springdoc 2.8.8 依赖commons-lang低版本存在CVE-2025-48924漏洞,升级版本解决
2025-08-08 11:55:08 +08:00
FAN-yeBandGitHub 7b72d517ab Merge pull request #1997 from xinnan-tech/rainv123-patch-1
feat:paddlespeech tts服务实现
2025-08-08 11:08:22 +08:00
FAN-yeB 1c43295fbc Merge branch 'rainv123-patch-1' of https://github.com/xinnan-tech/xiaozhi-esp32-server into rainv123-patch-1 2025-08-08 11:05:39 +08:00
FAN-yeB 747c432ea3 update:paddlespeech tts服务配置教程文档 2025-08-08 11:00:31 +08:00
rainv123andGitHub 235bdba42a paddlespeech-deploy.md
PaddleSpeechTTS服务集成xiaozhi服务
2025-08-08 10:59:07 +08:00
hrzandGitHub b3f603732e Merge pull request #1915 from YourKnight1/main
调整PaddleSpeechTTS流式传输
2025-08-07 23:42:16 +08:00
rainv123andGitHub 6091eae7fc paddlespeech-deploy.md
本地部署xiaozhi调用paddlespeechTTS服务
2025-08-07 17:07:52 +08:00
rainv123andGitHub ef36ad1ec1 paddlespeech-deploy.md 2025-08-07 16:50:10 +08:00
rainv123andGitHub f832611d41 Create paddlespeech-deploy.md
PaddleSpeechTTS流式传输集成到xiaozhi服务
2025-08-07 16:19:06 +08:00
Chingfeng Li 4d50eeb830 修正self错误 2025-08-06 14:53:59 +08:00
Chingfeng Li abf1fa0ef4 调整子类 2025-08-06 11:34:57 +08:00
Chingfeng Li 903c0e51ee 修正剩余文本 2025-08-06 10:13:42 +08:00
Chingfeng Li ce6a711c8e 调整流控参数优化 2025-08-06 10:09:28 +08:00
Chingfeng Li 095fe72db0 待完善流控方式 2025-08-05 18:35:37 +08:00
Chingfeng Li 1b6cd01eaf 增加流式处理方法 2025-08-05 14:53:52 +08:00
Chingfeng Li ac1e19f621 调整流式处理opus 2025-08-05 14:10:55 +08:00
Chingfeng Li faf2890695 增加流式处理 2025-08-04 17:04:46 +08:00
Chingfeng Li 75f0773247 增加流式处理Opus 2025-08-04 14:00:41 +08:00
Chingfeng Li 8b81b918cc 移除未使用的total_duration返回值 2025-08-04 13:49:12 +08:00
欣南科技andGitHub 5fe91a80fa Merge pull request #1976 from xinnan-tech/docker-install
Docker install
2025-08-03 16:20:49 +08:00
hrz 6f3dee74be update:优化自动docker全模块懒人脚本说明 2025-08-03 16:09:32 +08:00
hrzandGitHub 9391e8cc1a Merge pull request #1949 from VanillaNahida/main
refact: 重写了Docker全量部署的脚本的实现,支持一键安装一键配置一键更新镜像
2025-08-02 08:56:19 +08:00
hrzandGitHub 8aa5537029 Merge pull request #1963 from myifeng/fix-tts-message
移除无用的TTS消息
2025-08-02 08:55:12 +08:00
Chingfeng Li f40c3ec0f6 恢复sentence_start判断 2025-07-31 16:59:52 +08:00
hrzandGitHub 408143e55e Merge pull request #1962 from zebbingo/main
fix: bug state.type -> state['type']
2025-07-31 16:30:13 +08:00
Chingfeng Li 71eef4693d 仅在首句发送TTS消息 2025-07-31 14:15:39 +08:00
Han WuandGitHub 0b7814882d Merge pull request #1 from zebbingo/codex/find-and-fix-important-codebase-bug
Fix bug in hass_set_state plugin
2025-07-31 13:50:14 +08:00
Han Wu 99cf26ee9e Fix Home Assistant state handler 2025-07-31 13:38:46 +08:00
hrzandGitHub 775f754ff3 Merge pull request #1953 from yaotutu/feature/sherpa-paraformer-support
feat: 添加 Sherpa-ONNX Paraformer 模型支持
2025-07-31 13:35:03 +08:00
hrzandGitHub b6ee2dee68 Delete docs/sherpa-paraformer-guide.md 2025-07-31 13:34:17 +08:00
Chingfeng Li ef25e82544 移除无用的tts消息sentence_end 2025-07-31 13:21:29 +08:00
hrzandGitHub 14b7631dd6 Merge pull request #1958 from pursue-wind/fix_qwen_functioncall_err
fix: 修复qwen模型返回toolcall的arguments为None时导致后续请求400
2025-07-30 20:31:36 +08:00
hrzandGitHub ef0099b3c9 Update connection.py 2025-07-30 20:31:17 +08:00
VanillaNahida 38d60affce Docker安装镜像改为国内镜像源 2025-07-30 18:55:54 +08:00
chan c8a2c9bbd4 fix: 修复qwen模型返回toolcall的arguments为None时导致后续请求400 2025-07-30 18:53:48 +08:00
hrzandGitHub 9c2084b62e Merge pull request #1955 from xinnan-tech/py_fix_time
fix: 时间被固定
2025-07-30 17:33:01 +08:00
yaotutu 9edd083411 feat: 添加 Sherpa-ONNX Paraformer 模型支持
- 在 sherpa_onnx_local.py 中添加 model_type 参数,支持 paraformer 和 sense_voice 两种模型类型
- 在 config.yaml 中添加 SherpaParaformerASR 配置示例
- 添加详细的 Paraformer 使用文档 (docs/sherpa-paraformer-guide.md)
- 保持向后兼容,默认使用 sense_voice 模型

这个改动允许用户在低性能设备(如 RK3566)上使用更轻量的 Paraformer 模型,
相比 SenseVoice (894MB),Paraformer-small (78MB) 可以提供 4-6 倍的识别速度提升。
2025-07-30 10:41:27 +08:00
VanillaNahida 80e8ecc4f4 refact: 重写了Docker全量部署的脚本的实现,支持一键安装一键配置一键更新镜像 2025-07-30 03:00:37 +08:00
Sakura-RanChen e5d3048fb2 fix: 时间被固定 2025-07-29 09:55:32 +08:00
hrzandGitHub 67c4622ca7 Merge pull request #1935 from myifeng/aliyun-internal
如果配置的是ECS内网地址,则使用ws协议,默认是wss协议
2025-07-29 08:59:34 +08:00
欣南科技andGitHub 121f1c4698 Merge pull request #1936 from xinnan-tech/hot-fix
update:优化最后活动时间
2025-07-28 22:40:21 +08:00
hrz ff9fb9eb1b update:优化最后活动时间 2025-07-28 22:38:48 +08:00
Chingfeng Li b07a8796ff 阿里云ECS内网访问协议设置 2025-07-28 17:06:53 +08:00
hrzandGitHub 8ad5ff457e Merge pull request #1932 from xingxinonline/fix/manager-api-voice-print
fix: 增加时间排序,修复增加声纹注册获取对话记录不是最新对话的问题
2025-07-28 15:14:04 +08:00
gitjianyuandGitHub 99f6209c57 Update AgentChatHistoryServiceImpl.java
添加降序排序,
1.改用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
 2.id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
2025-07-28 14:12:42 +08:00
gitjianyuandGitHub 1b3a55b105 Update VoicePrintDialog.vue
删除监听智能体id的,从原先的打开声纹页面获取一次对话,改为每次打开增加修改页面,从新获取一次新的对话信息
2025-07-28 13:58:06 +08:00
醒心onlineandGitHub 8b59a94324 Merge branch 'xinnan-tech:main' into fix/manager-api-voice-print 2025-07-28 12:01:41 +08:00
xingxinonline b17f20eece feat: 在对话框可见性变化时获取最近的50条记录 2025-07-28 11:51:45 +08:00
xingxinonline d9062a0bb0 fix: 增加时间排序,修复增加声纹注册获取对话记录不是最新对话的问题
https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/1922
2025-07-28 11:20:39 +08:00
欣南科技andGitHub 6066c20676 Merge pull request #1923 from xinnan-tech/update-version
update:升级版本号
2025-07-26 11:47:15 +08:00
hrz 1c7ba50def update:升级版本号 2025-07-26 11:46:11 +08:00
hrzandGitHub e53b24ef47 Merge pull request #1901 from xinnan-tech/py_test_tts
Py test tts
2025-07-25 22:17:59 +08:00
hrz 1a978abcc1 update: MinimaxTTSHTTPStream和MinimaxTTSWebSocketStream还在测试,测试完再开放 2025-07-25 22:11:30 +08:00
hrzandGitHub b91f4e4281 Merge pull request #1911 from xinnan-tech/manager-api-agent-optimize
优化
2025-07-25 21:47:54 +08:00
hrz 86978329eb update:优化逻辑 2025-07-25 21:47:21 +08:00
欣南科技andGitHub 337ecf0efe Merge pull request #1918 from xinnan-tech/update-doc
update:调整位置
2025-07-25 11:38:06 +08:00
hrz 4c3eb90bfc update:调整位置 2025-07-25 11:37:29 +08:00
欣南科技andGitHub 018a0422b7 Merge pull request #1916 from xinnan-tech/update-doc
update:更新全模块关系图
2025-07-25 11:29:56 +08:00
hrz f18ac169fa update:更新全模块关系图 2025-07-25 11:29:23 +08:00
root 04746961f2 调整PaddleSpeechTTS流式传输 2025-07-25 10:09:04 +08:00
JianYu Zheng 3939c81044 添加:删除模型的验证
--ModelConfigServiceImpl.java 删除视觉模型时,如果引用了,就不能删除
2025-07-24 17:13:05 +08:00
JianYu Zheng fdbe5fa556 添加了修改模型配置验证
--ModelConfigServiceImpl.java 修改模型,jsonConfig 里包括llm,校验一下llm是否时在LLM模型里,并且这个模型的供应器类型只能是openai和ollama类型
2025-07-24 17:01:43 +08:00
hrz dff8b8ccec Merge branch 'main' into py_test_tts 2025-07-24 16:18:39 +08:00
JianYu Zheng 8d11b47241 添加了新请求接口,大语言模型和意图参数关系的选择判断
--model.js 添加新接口方法
--roleConfig.vue 添加智能体配置,当选择的LLM不是openai、ollama类型时,意图模型不能选择“无意图识别“
2025-07-24 16:17:27 +08:00
hrz 78e5c52932 Merge branch 'main' into py_test_tts 2025-07-24 15:15:44 +08:00
CGDandGitHub 46c7759718 Merge pull request #1909 from xinnan-tech/py_fix_emoji
fix: 补充None值判断
2025-07-24 15:09:14 +08:00
Sakura-RanChen 2f5e8c2019 fix: 补充None值判断 2025-07-24 15:07:35 +08:00
hrz 2508d3f965 update:获取nginx转发的ip地址 2025-07-24 14:38:56 +08:00
hrzandGitHub 3699d28dd0 Merge pull request #1893 from Hmmrrr/main
feat:添加minimax单向双向流式
2025-07-24 14:18:19 +08:00
JianYu Zheng be7146fa89 添加新接口,获取llm模型基础模型信息
--LlmModelBasicInfoDTO.java 新的llm模型基础模型信息返回对象
--ModelConfigService.java
--ModelConfigServiceImpl.java
--ModelController.java
定义,实现,添加了接口。一个获取获取LLM模型信息的:信息内容包括id,名称,type
2025-07-24 11:50:15 +08:00
CGDandGitHub 66f4ea0a84 Merge pull request #1905 from xinnan-tech/py_fix_emoji
fix: LLM携带空文本时错误判断
2025-07-24 09:33:19 +08:00
Sakura-RanChen 84ff897b46 fix: LLM携带空文本时错误判断 2025-07-24 09:31:44 +08:00
JianYu Zheng 29c7b2a920 修改:保存智能体配置方法
--AgentServiceImpl.java 保存智能体配置方法,添加`大语言模型和意图识别是否符合匹配`的参数验证
2025-07-23 15:59:22 +08:00
Sakura-RanChen 83ded8458a Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-23 14:40:14 +08:00
Sakura-RanChen e93053d412 fix: 文本重复发送 2025-07-23 14:35:59 +08:00
欣南科技andGitHub 2370936dfd Merge pull request #1899 from xinnan-tech/manager-api-mcp
优化获取请求mcp工具方法
2025-07-23 14:15:44 +08:00
JianYu Zheng 06bd7aed36 优化获取请求mcp工具方法
--JsonRpcTwo.java 添加JSON-RPC2.0 格式规范对象
--XiaoZhiMcpJsonRpcJson.java 添加小智mcp JSON-RPC 2.0 请求内容json 常量
--McpJsonRpcRequest.java 删除之前的mcp JSON-RPC2.0 构造对象
--AgentMcpAccessPointServiceImpl.java
优化思路,每次发送请求都要构造2次对象和转换2次json请求内容,且每次内容都一样,现在把最终转成的json请求内容存储为常量,所有请求共用这些常量,减少每次请求构造和转换
2025-07-23 11:56:36 +08:00
欣南科技andGitHub ff560a317c Merge pull request #1894 from xinnan-tech/update-doc
update:添加阿里云流式推荐
2025-07-22 18:22:05 +08:00
hrz 6892befa15 update:添加阿里云流式推荐 2025-07-22 18:21:09 +08:00
Hmmrrr fadf18b7fc feat:添加minimax单向双向流式 2025-07-22 18:13:05 +08:00
hrzandGitHub d9b632c031 Merge pull request #1892 from xinnan-tech/py_fix_mcp
update:添加Home Assistant插件超时处理和错误日志
2025-07-22 17:43:33 +08:00
hrzandGitHub 35fd4d2770 Merge pull request #1890 from xinnan-tech/manager-api-mcp
修复mcp工具太多会获取不到内容的bug
2025-07-22 16:45:05 +08:00
3030332422 1b8963f311 update:添加Home Assistant插件超时处理和错误日志 2025-07-22 16:28:12 +08:00
欣南科技andGitHub 710218b5b5 Merge pull request #1716 from xinnan-tech/py_test_tts
update:添加阿里云流式asr
2025-07-22 13:27:41 +08:00
hrz 60cbe1571c update:修复cosyvoice非流式合成方法 2025-07-22 13:25:54 +08:00
hrzandGitHub 6740d6a723 Merge pull request #1885 from xinnan-tech/py_prompt_emoji
Py prompt emoji
2025-07-21 23:18:30 +08:00
hrzandGitHub eb14e50941 Merge pull request #1882 from pupa880/main
添加最简化配置时配置声纹的教程
2025-07-21 23:14:08 +08:00
hrzandGitHub 37ac778ff4 Update voiceprint-integration.md 2025-07-21 23:13:45 +08:00
Sakura-RanChen bc5586a077 update: HuoshanTTS服务器资源释放 2025-07-21 17:46:22 +08:00
JianYu Zheng eb7ac93e72 修复mcp工具太多会获取不到内容的bug
--WebSocketClientManager.java
1.构建websocket对象的时候,添加可以设置缓冲区大小
2.断开连接的日志添加断开原因的内容
--AgentMcpAccessPointServiceImpl.java
1.设置每个websocket的缓冲区为1m,bug原因:缓存区太小,数据量太大,导致直接断开连接
2025-07-21 16:07:56 +08:00
pupa bf8a8bddf1 添加最简化配置时配置声纹的教程 2025-07-21 14:13:54 +08:00
hrz d1badcb28c update:修改版本号 2025-07-21 13:29:17 +08:00
hrz f6e79e17b7 update:更正cosyvoice大模型节点 2025-07-21 12:03:53 +08:00
Sakura-RanChen eead126f7a update: 表情由llm发送,长文本进行约束 2025-07-21 09:30:23 +08:00
hrzandGitHub e5ef61dfd3 Merge pull request #1873 from xinnan-tech/py_test
修复声纹识别场景下的意图识别问题、日志文件的补充
2025-07-20 20:23:29 +08:00
hrz eb130aa57f Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-20 00:29:25 +08:00
hrz c9b10494d8 update:优化配置文件 2025-07-20 00:29:00 +08:00
3030332422 b8136ac1dc fix:修复阿里云流式ASR音频传递问题 2025-07-19 18:17:21 +08:00
3030332422 6d5b53fcd5 fix: 修复阿里云流式ASR终止协议缺失问题 2025-07-19 12:16:12 +08:00
hrz 5cf8eb5e71 update:优化OpenaiASR、GroqASR配置 2025-07-19 01:03:36 +08:00
3030332422 f037f4c7de fix: 修复声纹识别场景下的意图识别问题 2025-07-18 11:34:14 +08:00
3030332422 618be6d108 update:日志文件的补充 2025-07-17 16:08:13 +08:00
hrz 7fb028205f Merge branch 'main' into py_test_tts
# Conflicts:
#	main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
2025-07-17 14:32:50 +08:00
Sakura-RanChen a09e56bbfd Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-17 11:43:49 +08:00
Sakura-RanChen c0c01a0285 数据集回滚 2025-07-17 11:42:51 +08:00
hrzandGitHub af0854a840 Merge pull request #1812 from xinnan-tech/openai-asr
OpenAI asr
2025-07-17 11:31:54 +08:00
hrzandGitHub f70652f165 Merge branch 'py_test_tts' into openai-asr 2025-07-17 11:31:44 +08:00
hrzandGitHub 7c440f47b6 Merge pull request #1852 from xinnan-tech/py_fix_DoubaoStreamASR
修复关于声纹识别的若干问题
2025-07-17 11:22:59 +08:00
hrzandGitHub 6967cc2293 Merge pull request #1855 from pursue-wind/fix_mcp_proxy_err
fix: 修复mcp-proxy命令不存在
2025-07-17 11:22:28 +08:00
hrzandGitHub 97ad1f6f22 Merge pull request #1856 from xinnan-tech/manager-api-mcp
修改智控台删除声纹,声纹服务不在线,无法删除的业务逻辑错误
2025-07-17 11:07:49 +08:00
JianYu Zheng f62f530f97 优化
--AgentVoicePrintServiceImpl.java
 1.重写了构造器引入系统配置线程池对象
 2.删除的时候异步处理删除声纹数据的方法,加快删除接口的返回
2025-07-17 10:49:51 +08:00
JianYu Zheng 815647ae02 补充日志
--AgentVoicePrintServiceImpl.java
2025-07-17 09:49:05 +08:00
JianYu Zheng ce1e3fb04d 修改业务逻辑
--AgentVoicePrintServiceImpl.java 删除声纹的时间,声纹服务不在线或者删除失败,都允许删除
2025-07-17 09:45:27 +08:00
chan 1a86828ae6 fix: 修复mcp-proxy命令不存在 2025-07-17 09:23:41 +08:00
3030332422 8831ea22b7 update:删去一些重复、不用的代码 2025-07-16 16:43:49 +08:00
3030332422 1a411da133 fix:修复FunASR多设备连接时声纹识别配置相互覆盖的问题 2025-07-16 15:42:21 +08:00
Sakura-RanChen e2a7534aa5 update: 优化相关处理 2025-07-16 15:03:53 +08:00
欣南科技andGitHub f8a92e1710 Merge pull request #1845 from xinnan-tech/manager-api-mcp
优化mcp错误提示
2025-07-15 18:36:45 +08:00
3030332422 953a232654 fix: 修复使用豆包流式ASR时,不能使用声纹识别功能的问题。 2025-07-15 18:13:01 +08:00
JianYu Zheng 4afa29554f 优化map错误提示
--AgentMcpAccessPointServiceImpl.java 优化提示
2025-07-15 15:44:43 +08:00
3030332422 dc78057823 fix:修复多模块部署时远程ASR声纹识别功能未初始化的问题 2025-07-15 14:52:56 +08:00
欣南科技andGitHub 53ca586847 Merge pull request #1826 from xinnan-tech/py_fix_log
fix:日志优化的补充
2025-07-15 14:40:54 +08:00
欣南科技andGitHub 4f7e96ed7d Merge pull request #1838 from xinnan-tech/update-doc
update:更新文档
2025-07-15 14:39:47 +08:00
hrz 0397ef3fad update:更新文档 2025-07-15 13:09:04 +08:00
欣南科技andGitHub aa38591d90 Merge pull request #1837 from xinnan-tech/update-doc
update:增加开发团队信息
2025-07-15 12:00:10 +08:00
hrz b87b82bec9 update:增加开发团队信息 2025-07-15 11:59:13 +08:00
hrzandGitHub 2e719e2ccc Merge pull request #1830 from hsycc/main
fix: 修复配置声纹识别地址为域名不带端口, 请求url错误的问题
2025-07-14 23:16:54 +08:00
hsycc 909b41174b fix: 修复配置声纹识别地址为域名不带端口, 请求url错误的问题 2025-07-14 20:13:48 +08:00
Sakura-RanChen 2c33ee5b32 update: 优化文本处理 2025-07-14 17:34:42 +08:00
3030332422 82beae0e59 fix:日志优化的补充 2025-07-14 07:28:05 +08:00
欣南科技andGitHub 7f979e2f52 Merge pull request #1820 from xinnan-tech/update-doc
update:补充声纹识别部署细节
2025-07-12 17:43:59 +08:00
hrz e94eb302a8 update:补充声纹识别部署细节 2025-07-12 17:43:27 +08:00
欣南科技andGitHub 8ffbb614f7 Merge pull request #1819 from xinnan-tech/update-doc
update:补充声纹服务启动的细节
2025-07-12 16:50:10 +08:00
hrz 7a589f4a7b update:补充声纹服务启动的细节 2025-07-12 16:49:32 +08:00
Sakura-RanChen 04ed5ed980 update: 同步非流式处理 10秒超时链接不复用 2025-07-11 18:04:00 +08:00
欣南科技andGitHub e37b094d1e Merge pull request #1813 from xinnan-tech/py_fix_log
Py fix log
2025-07-11 17:29:49 +08:00
欣南科技andGitHub a491ce96cb Merge branch 'main' into py_fix_log 2025-07-11 17:29:37 +08:00
hrz 7be79519b1 update:增加声纹识别演示视频 2025-07-11 17:23:13 +08:00
3030332422 47e4c0f190 fix:日志文件的优化 2025-07-11 16:44:50 +08:00
Sakura-RanChen 8e5d933745 update: 优化音频播放 文本发送 2025-07-11 16:36:47 +08:00
欣南科技andGitHub 6b24844172 Merge pull request #1810 from xinnan-tech/hot-fix
update:优化总结
2025-07-11 15:43:39 +08:00
hrz c6225f3ab8 update:优化总结 2025-07-11 15:42:46 +08:00
欣南科技andGitHub 60c4aa943b Merge pull request #1807 from xinnan-tech/hot-fix
add:声纹识别开启教程
2025-07-11 14:14:32 +08:00
hrz 81d0adf73c add:声纹识别开启教程 2025-07-11 14:13:39 +08:00
欣南科技andGitHub 7dc3929f0e Merge pull request #1806 from xinnan-tech/hot-fix
update:优化声纹识别有结果时内容的展示
2025-07-11 12:15:05 +08:00
hrz 09be3a4de3 update:优化声纹识别有结果时内容的展示 2025-07-11 12:14:25 +08:00
欣南科技andGitHub f7407e46c5 Merge pull request #1805 from xinnan-tech/hot-fix
update:修复二次声纹注册bug
2025-07-11 11:57:25 +08:00
hrz 8f685967d3 update:修复二次声纹注册bug 2025-07-11 11:49:53 +08:00
Sakura-RanChen 23cd453af8 update: aliyun双流改造 待优化长连接机制和文本生成反馈为空 2025-07-11 11:36:58 +08:00
欣南科技andGitHub 8f6dd22a6d Merge pull request #1804 from xinnan-tech/mangger-api-voice-print
声纹识别
2025-07-11 10:19:15 +08:00
hrz 108c87f395 update:优化提示词 2025-07-11 10:15:14 +08:00
hrz cd58b3a39a update:发行新版本 2025-07-11 09:14:34 +08:00
Sakura-RanChen 26a3b51162 fix: 补充文档 2025-07-10 16:07:06 +08:00
3030332422 2fbee33185 update:python对接声纹识别优化 2025-07-10 15:03:49 +08:00
hrz 6da3138814 Merge branch 'main' into py_test_tts 2025-07-10 13:47:53 +08:00
luruxian b2e6156bbb chore(db): 添加新的数据库变更集202507101201
添加新的数据库变更脚本202507101201.sql到变更日志中
2025-07-10 11:44:49 +08:00
luruxian fbd4a22e3e feat(asr): 添加OpenAI和Groq语音识别支持
新增GPT语音识别模型供应器和配置,实现语音转文本功能
更新相关文档说明,包含API申请步骤和使用注意事项
2025-07-10 11:41:20 +08:00
Sakura-RanChen dede73e9a5 fix: id一致 方法流程 2025-07-10 11:34:31 +08:00
JianYu Zheng f499040955 Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-10 11:33:28 +08:00
JianYu Zheng 008c446966 添加判断
--AgentVoicePrintServiceImpl.java
 1.声纹音频保存,增加了保存前识别一下声纹是否有相似的声音,有则不保存
 2.声纹音频修改,增加了保存前识别一下声纹是否有相似的声音且相似声纹id和本次修改id不一样的,则不修改
 3.添加了识别声纹http的请求的方法
 --IdentifyVoicePrintResponse.java 识别声纹响应类型
2025-07-10 11:20:32 +08:00
Sakura-RanChen ce358dcd65 fix: 音频播放方法跟随主分支 2025-07-10 10:17:07 +08:00
hrz 01158e67e0 add:智控台返回声纹接口信息 2025-07-10 09:54:16 +08:00
Sakura-RanChen d533fb83eb fix: 插入报错修复,跟随主分支新增列 2025-07-10 09:52:54 +08:00
f4cfa04954 update: TTS复用链接,VAD双阈值判断 (#1742)
* update: TTS复用链接,VAD双阈值判断

* fix: 播放音乐时,引导词卡顿

* update:优化引导词

* update: 优化chat函数流程 优化huoshan处理 会话保持一致性

* fix: 等待时可能已经完成(设置为None),后续对None错误访问

* update:增加书名号

* fix: 打断状态未重置 监听未完成时服务端可能还在发送数据 此时复用链接会接收上个语音的残余 需要两者一同关闭

---------

Co-authored-by: hrz <1710360675@qq.com>
2025-07-09 18:34:04 +08:00
JianYu Zheng ae4387ce3f Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-09 18:12:27 +08:00
JianYu Zheng a64b495083 增加判断
--AgentVoicePrintServiceImpl.java 添加,修改声纹音频的是增加了验证这个音频是否属于这个智能体
2025-07-09 17:31:34 +08:00
JianYu Zheng 81cdd0a211 添加新接口和2个新方法
--AgentChatHistoryService.java 添加‘根据音频数据ID获取聊天内容’和‘查询此音频id是否属于此智能体’的方法定义
--AgentChatHistoryServiceImpl.java 实现2个新方法
--AgentController.java 增加一个获取音频内容的接口
2025-07-09 17:29:36 +08:00
Sakura-RanChen 6bfb5f1340 Merge branch 'py_link_huoshan' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_link_huoshan 2025-07-09 17:05:02 +08:00
Sakura-RanChen 2625133f40 fix: 打断状态未重置 监听未完成时服务端可能还在发送数据 此时复用链接会接收上个语音的残余 需要两者一同关闭 2025-07-09 17:04:42 +08:00
hrzandGitHub 38409b4200 Merge pull request #1755 from xinnan-tech/py_test
update:python单模块部署声纹识别对接
2025-07-09 16:51:26 +08:00
hrzandGitHub 29b4d0daeb Merge branch 'mangger-api-voice-print' into py_test 2025-07-09 16:51:10 +08:00
hrz 8f4f9fe19a update:优化提示词 2025-07-09 16:30:10 +08:00
hrz 7f67459c6e update:增加书名号 2025-07-09 16:26:26 +08:00
JianYu Zheng f47a7b4dbc 修复添加和修改声纹选择的时候,编辑的声纹不在里面,导致无法显示内容的问题
--agent.js 增加查询音频内容的接口
--VoicePrintDialog.vue 修复添加和修改声纹选择的时候,编辑的声纹不在里面,导致无法显示内容的问题
2025-07-09 15:56:07 +08:00
hrz 0bf6926506 update:优化提示词 2025-07-09 15:35:25 +08:00
3030332422 d13fb73c67 update:优化 2025-07-09 14:23:47 +08:00
JianYu Zheng 5e18a46e3f 添加新功能,声纹选择的时候可以听取音频文件
--VoicePrintDialog.vue 添加听取音频的功能
2025-07-09 11:48:02 +08:00
Sakura-RanChen 62e39dd0e2 fix: 等待时可能已经完成(设置为None),后续对None错误访问 2025-07-09 11:18:26 +08:00
hrzandGitHub 203ea89c6d Merge pull request #1788 from xinnan-tech/py_intent_fix
fix: delete未定义的方法报错
2025-07-08 17:54:17 +08:00
Sakura-RanChen 9ddd967776 fix: delete未定义的方法报错 2025-07-08 17:52:51 +08:00
Sakura-RanChen 44593ff3f3 update: 优化chat函数流程 优化huoshan处理 会话保持一致性 2025-07-08 17:48:50 +08:00
JianYu Zheng 3ed13b3ff5 修复漏洞
--AgentChatHistoryServiceImpl.java 用户聊天记录不一定都带有音频,查询时要排除不带音频的最近50条聊天记录
--AgentChatHistoryService.java 修改方法描述
2025-07-08 17:32:44 +08:00
JianYu Zheng 53cfdcc240 Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-08 17:15:10 +08:00
JianYu Zheng 0636b95b87 声纹表添加音频字段
--202507081646.sql 添加字段sql
--AgentVoicePrintEntity.java 对应实体更新
--db.changelog-master.yaml 执行添加字段sql的配置
2025-07-08 17:10:57 +08:00
hrzandGitHub cf449a9b60 Merge pull request #1787 from xinnan-tech/system-prompt
强化小智提示词
2025-07-08 17:00:46 +08:00
hrz ab890ada37 update:强化小智提示词 2025-07-08 16:59:38 +08:00
JianYu Zheng b9c136c6ae 修复错误
--VoicePrint.vue
 1.方法调用错误,修改方法名
 2.删除无用的多选框
2025-07-08 16:55:02 +08:00
JianYu Zheng f81a7e8273 智能体设置卡片添加设置声纹的按钮
--DeviceItem.vue
 1.添加声纹设置按钮
 2.添加跳转到声纹页面方法
--home.vue 修复因为添加新按钮导致卡片按钮文字换行样式问题
2025-07-08 16:18:29 +08:00
JianYu Zheng d40d4a08b0 添加声纹管理页面
--VoicePrint.vue 声纹展示页面
--VoicePrintDialog.vue 声纹增改组件
--index.js 注册声纹页面路径
2025-07-08 16:15:52 +08:00
JianYu Zheng 61907d2270 添加新接口方法
--agent.js 添加关于声纹和获取用户列表的接口
 1.添加智能体的声纹
 2.获取指定智能体声纹列表
 3.删除智能体声纹
 4.更新智能体声纹
 5.获取指定智能体用户类型聊天记录
2025-07-08 16:13:13 +08:00
JianYu Zheng 601a90981e 修复错误
--AgentVoicePrintServiceImpl.java
 1.修复事务处理过程出错,返回错误无法被前端页面处理
 2.修复更新声纹方法,更正错误sql
2025-07-08 15:48:20 +08:00
JianYu Zheng 10b7259b13 添加新接口:查看指定智能体最近50条用户聊天记录
--AgentChatHistoryService.java 指定智能体最近50条用户聊天记录定义
--AgentChatHistoryServiceImpl.javaAgentChatHistoryService.java 实现指定智能体最近50条用户聊天记录方法
--AgentChatHistoryType.java 添加智能体聊天记录类型枚举
--AgentChatHistoryUserVO.java 展示用户聊天记录vo
--AgentController.java 添加新接口
2025-07-08 15:44:43 +08:00
CGD 5c83d63fe2 update:声纹识别对接的优化 2025-07-08 11:25:54 +08:00
hrzandGitHub beb7a191c1 Merge pull request #1777 from jlau-ice/feat/add-password-visibility
feat: 登录注册等页面密码输入框添加可见属性
2025-07-07 21:29:10 +08:00
ganbin ee72a328ca feat: 登录注册等页面密码输入框添加可见属性 2025-07-07 18:50:03 +08:00
JianYu Zheng c85419bf06 添加添加声纹增删改查接口
--AgentVoicePrintController.java 添加接口
2025-07-07 15:33:16 +08:00
CGD 155ba92ba6 update:增加了智控台的sql语句,优化了部分配置 2025-07-07 15:29:13 +08:00
JianYu Zheng b201c3cca8 添加数据认证,需要登录用户是此数据所有者才有权限修改,查询,删除
--AgentVoicePrintService.java 修改方法定义
--AgentVoicePrintServiceImpl.java
 1.需要登录用户是此数据所有者才有权限修改,查询,删除
 2.修改发送注册声纹http请求和发送注销声纹的请求的错误说明和添加日志记录
2025-07-07 15:11:37 +08:00
hrzandGitHub c45f47faa0 Update:优化mcp工具读取顺序 (#1775)
* update:发布0.6.3版本

* update:升级sherpa_onnx版本

* update:补充自动更新教程

* update:优化mcp工具读取顺序

* update:优化
2025-07-07 15:04:32 +08:00
JianYu Zheng ee7ea7fca6 添加编程事务,保证数据的统一,提取公共方法
--AgentVoicePrintServiceImpl.java
 1.为保存,修复,删除声纹添加事务
 2.提取发送注销声纹的请求为公共方法
2025-07-07 14:12:28 +08:00
hrzandGitHub 5ee7830f1b Merge pull request #1770 from jlau-ice/fix/vllm-reply-in-English
fix: 修正VLLM本地调用,question参数为空会用英文回复的问题
2025-07-07 11:58:58 +08:00
hrz 8da9dc8a3f update:优化引导词 2025-07-07 11:45:37 +08:00
JianYu Zheng 61ecfd2d92 实现智能体声纹服务层定义的‘获取指定智能体的所有声纹数据’和‘更新智能体的指的声纹数据’定义(未测试)
--AgentVoicePrintServiceImpl.java 实现方法
 1.获取指定智能体的所有声纹数据
 2.更新智能体的指的声纹数据
 3.提取共用方法(获取声纹音频资源数据,发送注册声纹http请求)
2025-07-07 10:58:35 +08:00
ganbin 2535ff6c90 fix: 修正VLLM本地调用,question参数为空会用英文回复的问题 2025-07-07 09:02:59 +08:00
hrzandGitHub 880bc6326a update:补充自动更新教程 (#1766)
* update:发布0.6.3版本

* update:升级sherpa_onnx版本

* update:补充自动更新教程
2025-07-06 15:40:02 +08:00
hrzandGitHub d892212583 Update version (#1765)
* update:发布0.6.3版本

* update:升级sherpa_onnx版本
2025-07-05 23:58:47 +08:00
hrzandGitHub 089acd357d update:发布0.6.3版本 (#1764) 2025-07-05 23:50:26 +08:00
hrzandGitHub d14f6937fa Merge pull request #1753 from myifeng/main
客户端连接成功时不主动发送hello
2025-07-05 20:05:51 +08:00
hrzandGitHub d74813ec80 Update connection.py 2025-07-05 20:05:36 +08:00
hrzandGitHub 1cfba1aa52 update: 单模块部署也能使用mcp接入点 (#1763)
* 优化:OTA接口需要适应最小示例参数 #1757

* update:修复智能体选择的模型不存在导致的500错误 #1752

* update: 单模块部署也能使用mcp接入点
2025-07-05 19:46:04 +08:00
marlonz 5f96f56574 remove gitignore 2025-07-05 13:50:47 +09:30
marlonz 076aaae944 mqtt gateway project 2025-07-05 13:47:49 +09:30
marlonz aed54bb3a6 tech review change 2025-07-05 13:30:14 +09:30
marlonz 7c03677295 update change log 2025-07-05 13:25:42 +09:30
marlonz 3d3875f14f update lq base sql 2025-07-05 12:34:57 +09:30
grincoolandGitHub f6dd493997 Merge pull request #1758 from grincool/xiaozhi-mqtt-dev
提交mqtt后端修改
2025-07-05 12:27:35 +09:30
grincoolandGitHub d5e3989ed1 Merge branch 'mqtt' into xiaozhi-mqtt-dev 2025-07-05 12:24:59 +09:30
JianYu Zheng 77527d8823 实现智能体声纹服务层定义的‘添加智能体新的声纹’和‘删除智能体的指的声纹’定义
--AgentVoicePrintServiceImpl.java 实现方法
 1.添加智能体新的声纹
 2.删除智能体的指的声纹
 3.共用方法(获取生纹接口URI对象,获取声纹地址基础路径,获取验证Authorization)
2025-07-04 18:04:39 +08:00
JianYu Zheng 5e5ec98995 添加智能体声纹实体,vo,dto,dao,service类
--AgentVoicePrintVO.java 智能体声纹展示vo
--AgentVoicePrintService.java 智能体声纹服务层定义
 1.添加智能体新的声纹
 2.删除智能体的指的声纹
 3.获取指定智能体的所有声纹数据
 4.更新智能体的指的声纹数据
--AgentVoicePrintSaveDTO.java 智能体声纹保存dto
--AgentVoicePrintUpdateDTO.java 智能体声纹修改dto
--AgentVoicePrintEntity.java 智能体声纹实体
--AgentVoicePrintDao.java 智能体声纹持久层
2025-07-04 18:02:19 +08:00
JianYu Zheng 1c2d4f9045 添加新表:智能体声纹表
--202507041018.sql 建表sql语句
--db.changelog-master.yaml 配置文件里添加执行新sql的配置
2025-07-04 17:54:41 +08:00
CGD 3d64152dba update:python单模块部署声纹识别对接 2025-07-04 17:11:44 +08:00
hrzandGitHub 37ecd3da1e 增加docker部署时url地址的说明 (#1754)
* update:优化mcp工具较多时的现实

* update:修改版本号

* update:docker部署manager-api,填写备注
2025-07-04 17:02:19 +08:00
myifeng 22e9e4c352 客户端连接成功时不主动发送hello 2025-07-04 15:27:38 +08:00
Sakura-RanChen 7e5a2b4549 fix: 播放音乐时,引导词卡顿 2025-07-04 11:27:49 +08:00
JianYu Zheng 036dde5bb0 添加新的参数:声纹接口地址
--202507031602.sql 添加声纹接口地址参数sql
--Constant.java 对应新参数的常量
--SysParamsController.java 验证声纹接口地址参数是否符合规则
--db.changelog-master.yaml 添加执行新的sql文件
2025-07-03 16:34:50 +08:00
Sakura-RanChen 69cac9d40a update: TTS复用链接,VAD双阈值判断 2025-07-03 09:16:06 +08:00
hrzandGitHub 04b132816c update:修改版本号
* update:优化mcp工具较多时的现实

* update:修改版本号
2025-07-01 18:16:21 +08:00
CGD 039badd265 update:添加了阿里云CosyVoice流式TTS 2025-07-01 18:14:36 +08:00
hrzandGitHub f08ed1bbb7 Merge pull request #1729 from xinnan-tech/fix-vllm
update:优化mcp工具较多时的显示
2025-07-01 17:31:58 +08:00
CGD 90a9631450 update:添加阿里云流式asr 2025-06-30 14:41:53 +08:00
marlonz b7c0201ca3 提交mqtt后端修改 2025-06-17 16:40:17 +09:30
359 changed files with 43679 additions and 5029 deletions
+12 -1
View File
@@ -31,6 +31,9 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: |
network=host
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
@@ -60,6 +63,10 @@ jobs:
tags: |
${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:server_{1},ghcr.io/{0}:server_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:server_latest', github.repository) }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_PROGRESS=plain
# 构建 manager-api 镜像
- name: Build and push manager-web
@@ -70,4 +77,8 @@ jobs:
push: true
tags: |
${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:web_{1},ghcr.io/{0}:web_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:web_latest', github.repository) }}
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_PROGRESS=plain
+9
View File
@@ -174,3 +174,12 @@ main/xiaozhi-server/mysql
uploadfile
*.json
.vscode
.cursor
!package.json
!**/package.json
# Do not ignore env and json files inside manager-mobile
!main/manager-mobile/**/env/
!main/manager-mobile/**/.env*
!main/manager-mobile/**/*.json
+10 -2
View File
@@ -3,10 +3,17 @@ FROM python:3.10-slim AS builder
WORKDIR /app
# 配置pip使用国内镜像源(阿里云)并设置超时和重试
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \
pip config set global.trusted-host mirrors.aliyun.com && \
pip config set global.timeout 120 && \
pip config set install.retries 5
COPY main/xiaozhi-server/requirements.txt .
# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt
# 安装Python依赖,使用并行下载
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
pip install --no-cache-dir -r requirements.txt --default-timeout=120 --retries 5
# 第二阶段:生产镜像
FROM python:3.10-slim
@@ -21,6 +28,7 @@ RUN apt-get update && \
# 从构建阶段复制Python包和前端构建产物
COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY --from=builder /usr/local/bin/mcp-proxy /usr/local/bin/mcp-proxy
# 复制应用代码
COPY main/xiaozhi-server .
+3 -3
View File
@@ -18,12 +18,12 @@ FROM bellsoft/liberica-runtime-container:jre-21-glibc
# 安装Nginx和字体库
RUN apk update && \
apk add --no-cache nginx bash && \
apk add --no-cache fontconfig ttf-dejavu msttcorefonts-installer && \
apk add --no-cache nginx bash fontconfig ttf-dejavu && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ msttcorefonts-installer || true && \
rm -rf /var/cache/apk/*
# 更新字体缓存
RUN printf 'YES\n' | update-ms-fonts && fc-cache -f -v
RUN (printf 'YES\n' | update-ms-fonts || true) && fc-cache -f -v
# 配置Nginx
COPY docs/docker/nginx.conf /etc/nginx/nginx.conf
+54 -24
View File
@@ -3,10 +3,10 @@
<h1 align="center">小智后端服务xiaozhi-esp32-server</h1>
<p align="center">
本项目为开源智能硬件项目
本项目基于人机共生智能理论和技术研发智能终端软硬件体系<br/>为开源智能硬件项目
<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a>提供后端服务<br/>
根据<a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">小智通信协议</a>使用Python、Java、Vue实现<br/>
帮助您快速搭建小智服务器
支持MQTT+UDP协议、Websocket协议、MCP接入点、声纹识别
</p>
<p align="center">
@@ -37,6 +37,14 @@
</a>
</p>
<p align="center">
Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
</br>
刘思源教授团队主导研发(华南理工大学)
</br>
<img src="./docs/images/hnlg.jpg" alt="华南理工大学" width="50%">
</p>
---
## 适用人群 👥
@@ -49,9 +57,9 @@
<table>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
<a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
<picture>
<img alt="小智esp32连接自己的后台模型" src="docs/images/demo1.png" />
<img alt="小智医疗救护车场景" src="docs/images/demo1.png" />
</picture>
</a>
</td>
@@ -86,7 +94,7 @@
</tr>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1Vy96YCE3R" target="_blank">
<a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
<picture>
<img alt="自定义音色" src="docs/images/demo6.png" />
</picture>
@@ -144,13 +152,18 @@
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<picture>
<img alt="MCP接入点" src="docs/images/demo13.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
<picture>
<img alt="声纹识别" src="docs/images/demo14.png" />
</picture>
</a>
</td>
</tr>
</table>
@@ -176,13 +189,15 @@
| 部署方式 | 特点 | 适用场景 | 部署文档 | 配置要求 | 视频教程 |
|---------|------|---------|---------|---------|---------|
| **最简化安装** | 智能对话、IOT、MCP、视觉感知 | 低配置环境,数据存储在配置文件,无需数据库 | [①Docker版](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②源码部署](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 如果使用`FunASR`要2核4G,如果全API,要2核2G | - |
| **全模块安装** | 智能对话、IOT、MCP接入点、视觉感知、OTA、智控台 | 完整功能体验,数据存储在数据库 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
| **全模块安装** | 智能对话、IOT、MCP接入点、声纹识别、视觉感知、OTA、智控台 | 完整功能体验,数据存储在数据库 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
常见问题及相关教程,可参考[这个链接](./docs/FAQ.md)
> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据
> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据
```
智控台地址: https://2662r3426b.vicp.fun
智控台(h5版): https://2662r3426b.vicp.fun/h5/index.html
服务测试工具: https://2662r3426b.vicp.fun/test/
OTA接口地址: https://2662r3426b.vicp.fun/xiaozhi/ota/
@@ -201,35 +216,37 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| 模块名称 | 入门全免费设置 | 流式配置 |
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍FunASRServer 或 👍DoubaoStreamASR |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍DoubaoLLM(火山doubao-1-5-pro-32k-250115) |
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
如果您关心各组件的耗时,请查阅[小智各组件性能测试报告](https://github.com/xinnan-tech/xiaozhi-performance-research),可按报告中的测试方法在您的环境中实际测试。
#### 🔧 测试工具
本项目提供以下测试工具,帮助您验证系统和选择合适的模型:
| 工具名称 | 位置 | 使用方法 | 功能说明 |
|:---:|:---|:---:|:---:|
| 音频交互测试工具 | main》xiaozhi-server》test》test_page.html | 使用谷歌浏览器直接打开 | 测试音频播放和接收功能,验证Python端音频处理是否正常 |
| 模型响应测试工具1 | main》xiaozhi-server》performance_tester.py | 执行 `python performance_tester.py` | 测试ASR(语音识别)、LLM(大模型)、TTS(语音合成)三个核心模块的响应速度 |
| 模型响应测试工具2 | main》xiaozhi-server》performance_tester_vllm.py | 执行 `python performance_tester_vllm.py` | 测试VLLM(视觉模型)的响应速度 |
| 模型响应测试工具 | main》xiaozhi-server》performance_tester.py | 执行 `python performance_tester.py` | 测试ASR(语音识别)、LLM(大模型)、VLLM(视觉模型)、TTS(语音合成)三个核心模块的响应速度 |
> 💡 提示:测试模型速度时,只会测试配置了密钥的模型。
---
## 功能清单 ✨
### 已实现 ✅
![请参考-全模块安装架构图](docs/images/deploy2.png)
| 功能模块 | 描述 |
|:---:|:---|
| 核心服务架构 | 基于WebSocketHTTP服务器,提供完整的控制台管理和认证系统 |
| 语音交互系统 | 支持流式ASR(语音识别)、流式TTS(语音合成)、VAD(语音活动检测),支持多语言识别和语音处理 |
| 智能对话系统 | 支持多种LLM(大语言模型),实现智能对话 |
| 视觉感知系统 | 支持多种VLLM(视觉大模型),实现多模态交互 |
| 意图识别系统 | 支持LLM意图识别、Function Call函数调用,提供插件化意图处理机制 |
| 核心架构 | 基于MQTT+UDP网关、WebSocketHTTP服务器,提供完整的控制台管理和认证系统 |
| 语音交互 | 支持流式ASR(语音识别)、流式TTS(语音合成)、VAD(语音活动检测),支持多语言识别和语音处理 |
| 声纹识别 | 支持多用户声纹注册、管理和识别,与ASR并行处理,实时识别说话人身份并传递给LLM进行个性化回应 |
| 智能对话 | 支持多种LLM(大语言模型),实现智能对话 |
| 视觉感知 | 支持多种VLLM(视觉大模型),实现多模态交互 |
| 意图识别 | 支持LLM意图识别、Function Call函数调用,提供插件化意图处理机制 |
| 记忆系统 | 支持本地短期记忆、mem0ai接口记忆,具备记忆总结功能 |
| 工具调用 | 支持客户端IOT协议、客户MCP协议、服务端MCP协议、MCP接入点协议、自定义工具函数 |
| 管理后台 | 提供Web管理界面,支持用户管理、系统配置和设备管理 |
@@ -239,7 +256,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
### 正在开发 🚧
想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3)
想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3)。常见问题及相关教程,可参考[这个链接](./docs/FAQ.md)
如果你是一名软件开发者,这里有一份[《致开发者的公开信》](docs/contributor_open_letter.md),欢迎加入!
@@ -257,7 +274,6 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
---
## 本项目支持的平台/组件列表 📋
### LLM 语言模型
| 使用方式 | 支持平台 | 免费平台 |
@@ -267,6 +283,8 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| dify 接口调用 | Dify | - |
| fastgpt 接口调用 | Fastgpt | - |
| coze 接口调用 | Coze | - |
| xinference 接口调用 | Xinference | - |
| homeassistant 接口调用 | HomeAssistant | - |
实际上,任何支持 openai 接口调用的 LLM 均可接入使用。
@@ -286,8 +304,8 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| 使用方式 | 支持平台 | 免费平台 |
|:---:|:---:|:---:|
| 接口调用 | EdgeTTS、火山引擎豆包TTS、腾讯云、阿里云TTS、CosyVoiceSiliconflow、TTS302AI、CozeCnTTS、GizwitsTTS、ACGNTTS、OpenAITTS、灵犀流式TTS | 灵犀流式TTS、EdgeTTS、CosyVoiceSiliconflow(部分) |
| 本地服务 | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、MinimaxTTS | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、MinimaxTTS |
| 接口调用 | EdgeTTS、火山引擎豆包TTS、腾讯云、阿里云TTS、阿里云流式TTS、CosyVoiceSiliconflow、TTS302AI、CozeCnTTS、GizwitsTTS、ACGNTTS、OpenAITTS、灵犀流式TTS、MinimaxTTS、火山双流式TTS | 灵犀流式TTS、EdgeTTS、CosyVoiceSiliconflow(部分) |
| 本地服务 | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、Index-TTS、PaddleSpeech | Index-TTS、PaddleSpeech、FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3 |
---
@@ -304,7 +322,15 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| 使用方式 | 支持平台 | 免费平台 |
|:---:|:---:|:---:|
| 本地使用 | FunASR、SherpaASR | FunASR、SherpaASR |
| 接口调用 | DoubaoASR、FunASRServer、TencentASR、AliyunASR | FunASRServer |
| 接口调用 | DoubaoASR、Doubao流式ASR、FunASRServer、TencentASR、AliyunASR、Aliyun流式ASR、百度ASR、OpenAI ASR | FunASRServer |
---
### Voiceprint 声纹识别
| 使用方式 | 支持平台 | 免费平台 |
|:---:|:---:|:---:|
| 本地使用 | 3D-Speaker | 3D-Speaker |
---
@@ -314,6 +340,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:------:|:---------------:|:----:|:---------:|:--:|
| Memory | mem0ai | 接口调用 | 1000次/月额度 | |
| Memory | mem_local_short | 本地总结 | 免费 | |
| Memory | nomem | 无记忆模式 | 免费 | |
---
@@ -323,6 +350,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:------:|:-------------:|:----:|:-------:|:---------------------:|
| Intent | intent_llm | 接口调用 | 根据LLM收费 | 通过大模型识别意图,通用性强 |
| Intent | function_call | 接口调用 | 根据LLM收费 | 通过大模型函数调用完成意图,速度快,效果好 |
| Intent | nointent | 无意图模式 | 免费 | 不进行意图识别,直接返回对话结果 |
---
@@ -333,8 +361,10 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| <img src="./docs/images/logo_bailing.png" width="160"> | [百聆语音对话机器人](https://github.com/wwbin2017/bailing) | 本项目受[百聆语音对话机器人](https://github.com/wwbin2017/bailing)启发,并在其基础上实现 |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [十方融海](https://www.tenclass.com/) | 感谢[十方融海](https://www.tenclass.com/)为小智生态制定了标准的通讯协议、多设备兼容性方案及高并发场景实践示范;为本项目提供了全链路技术文档支持 |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [玄凤科技](https://github.com/Eric0308) | 感谢[玄凤科技](https://github.com/Eric0308)贡献函数调用框架、MCP通信协议及插件化调用机制的实现代码,通过标准化的指令调度体系与动态扩展能力,显著提升了前端设备(IoT)的交互效率和功能延展性 |
| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | 感谢[huangjunsen](https://github.com/huangjunsen0406) 贡献`智控台移动端`模块,实现了跨平台移动设备的高效控制与实时交互,大幅提升了系统在移动场景下的操作便捷性和管理效率 |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [汇远设计](http://ui.kwd988.net/) | 感谢[汇远设计](http://ui.kwd988.net/)为本项目提供专业视觉解决方案,用其服务超千家企业的设计实战经验,赋能本项目产品用户体验 |
| <img src="./docs/images/logo_qinren.png" width="160"> | [西安勤人信息科技](https://www.029app.com/) | 感谢[西安勤人信息科技](https://www.029app.com/)深化本项目视觉体系,确保整体设计风格在多场景应用中的一致性和扩展性 |
| <img src="./docs/images/logo_contributors.png" width="160"> | [代码贡献者](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | 感谢[所有代码贡献者](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors)贡献者,你们的付出让项目更加健壮和强大。 |
<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+131 -73
View File
@@ -3,17 +3,17 @@
<h1 align="center">Xiaozhi Backend Service xiaozhi-esp32-server</h1>
<p align="center">
This project provides backend services for the open-source smart hardware project
This project is based on human-machine symbiotic intelligence theory and technology to develop intelligent terminal hardware and software systems<br/>providing backend services for the open-source intelligent hardware project
<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
Implemented using Python, Java, and Vue according to the <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Xiaozhi Communication Protocol</a><br/>
Helps you quickly set up your Xiaozhi server
Supports MCP endpoints and voiceprint recognition
</p>
<p align="center">
<a href="./README.md">中文</a>
· <a href="./docs/FAQ.md">FAQ</a>
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Report Issues</a>
· <a href="./README_en.md#deployment-documentation">Deployment Guide</a>
· <a href="./README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Deployment Docs</a>
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Release Notes</a>
</p>
<p align="center">
@@ -37,41 +37,49 @@ Helps you quickly set up your Xiaozhi server
</a>
</p>
<p align="center">
Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
</br>
刘思源教授团队主导研发(华南理工大学)
</br>
<img src="./docs/images/hnlg.jpg" alt="South China University of Technology" width="50%">
</p>
---
## Target Users 👥
This project requires ESP32 hardware devices. If you have purchased ESP32-related hardware, successfully connected to Brother Xia's backend service, and want to set up your own `xiaozhi-esp32` backend service, then this project is perfect for you.
This project requires ESP32 hardware devices to work. If you have purchased ESP32-related hardware, successfully connected to Brother Xia's deployed backend service, and want to build your own `xiaozhi-esp32` backend service independently, then this project is perfect for you.
Want to see it in action? Check out these videos 🎥
Want to see the usage effects? Click the videos below 🎥
<table>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
<picture>
<img alt="Xiaozhi esp32 connecting to own backend model" src="docs/images/demo1.png" />
<img alt="Xiaozhi ESP32 connecting to own backend model" src="docs/images/demo1.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
<picture>
<img alt="Custom voice" src="docs/images/demo2.png" />
<img alt="Custom voice timbre" src="docs/images/demo2.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
<picture>
<img alt="Using Cantonese" src="docs/images/demo3.png" />
<img alt="Using Cantonese for communication" src="docs/images/demo3.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
<picture>
<img alt="Control home appliances" src="docs/images/demo5.png" />
<img alt="Controlling home appliances" src="docs/images/demo5.png" />
</picture>
</a>
</td>
@@ -85,16 +93,16 @@ Want to see it in action? Check out these videos 🎥
</tr>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1Vy96YCE3R" target="_blank">
<a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
<picture>
<img alt="Custom voice" src="docs/images/demo6.png" />
<img alt="Custom voice timbre" src="docs/images/demo6.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
<picture>
<img alt="Play music" src="docs/images/demo7.png" />
<img alt="Playing music" src="docs/images/demo7.png" />
</picture>
</a>
</td>
@@ -108,14 +116,14 @@ Want to see it in action? Check out these videos 🎥
<td>
<a href="https://www.bilibili.com/video/BV178XuYfEpi" target="_blank">
<picture>
<img alt="IOT command control" src="docs/images/demo9.png" />
<img alt="IOT command control devices" src="docs/images/demo9.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV17LXWYvENb" target="_blank">
<picture>
<img alt="News broadcast" src="docs/images/demo0.png" />
<img alt="News broadcasting" src="docs/images/demo0.png" />
</picture>
</a>
</td>
@@ -131,7 +139,7 @@ Want to see it in action? Check out these videos 🎥
<td>
<a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
<picture>
<img alt="Photo recognition" src="docs/images/demo12.png" />
<img alt="Photo recognition of objects" src="docs/images/demo12.png" />
</picture>
</a>
</td>
@@ -143,20 +151,29 @@ Want to see it in action? Check out these videos 🎥
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<picture>
<img alt="MCP endpoint" src="docs/images/demo13.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
<picture>
<img alt="Voiceprint recognition" src="docs/images/demo14.png" />
</picture>
</a>
</td>
</tr>
</table>
---
## Warning ⚠️
## Warnings ⚠️
1. This project is open-source software. This software has no commercial relationship with any third-party API service providers (including but not limited to speech recognition, large models, speech synthesis, and other platforms) and does not provide any form of guarantee for their service quality or financial security.
It is recommended that users prioritize service providers with relevant business licenses and carefully read their service agreements and privacy policies. This software does not host any account keys, does not participate in fund transfers, and does not bear the risk of recharge fund losses.
1. This project is open-source software. This software has no commercial partnership with any third-party API service providers (including but not limited to speech recognition, large models, speech synthesis, and other platforms) that it interfaces with, and does not provide any form of guarantee for their service quality or financial security. It is recommended that users prioritize service providers with relevant business licenses and carefully read their service agreements and privacy policies. This software does not host any account keys, does not participate in fund flows, and does not bear the risk of recharge fund losses.
2. This project's functionality is not complete and has not passed network security testing. Please do not use it in production environments. If you deploy this project for learning in a public network environment, please ensure necessary protection measures are in place.
2. The functionality of this project is not complete and has not passed network security assessment. Please do not use it in production environments. If you deploy this project for learning purposes in a public network environment, please ensure necessary protection measures are in place.
---
@@ -164,18 +181,20 @@ It is recommended that users prioritize service providers with relevant business
![Banners](docs/images/banner2.png)
This project provides two deployment methods. Please choose according to your specific needs:
This project provides two deployment methods. Please choose based on your specific needs:
#### 🚀 Deployment Method Selection
| Deployment Method | Features | Suitable Scenarios | Deployment Guide | Requirements | Video Tutorial |
| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
|---------|------|---------|---------|---------|---------|
| **Simplified Installation** | Smart dialogue, IOT functionality, data stored in configuration files | Low-configuration environment, no database needed | [Docker Version](./docs/Deployment.md#method-1-docker-server-only) / [Source Code Deployment](./docs/Deployment.md#method-2-local-source-code-server-only) | 2 cores 4G if using `FunASR`, 2 cores 2G if using all APIs | - |
| **Full Module Installation** | Smart dialogue, IOT, OTA, Control Panel, data stored in database | Complete functionality experience | [Docker Version](./docs/Deployment_all.md#method-1-docker-full-modules) / [Source Code Deployment](./docs/Deployment_all.md#method-2-local-source-code-full-modules) | 4 cores 8G if using `FunASR`, 2 cores 4G if using all APIs | [Local Source Code Startup Video Tutorial](https://www.bilibili.com/video/BV1wBJhz4Ewe) / [Local Source Code Auto-Update Tutorial](./docs/dev-ops-integration.md) |
| **Simplified Installation** | Intelligent dialogue, IOT, MCP, visual perception | Low-configuration environments, data stored in config files, no database required | [Docker Version](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [Source Code Deployment](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 cores 4GB if using `FunASR`, 2 cores 2GB if all APIs | - |
| **Full Module Installation** | Intelligent dialogue, IOT, MCP endpoints, voiceprint recognition, visual perception, OTA, intelligent control console | Complete functionality experience, data stored in database |[Docker Version](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [Source Code Deployment](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③Source Code Deployment Auto-Update Tutorial](./docs/dev-ops-integration.md) | 4 cores 8GB if using `FunASR`, 2 cores 4GB if all APIs| [Local Source Code Startup Video Tutorial](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
> 💡 Note: Below are the test platforms deployed with the latest code. You can flash and test if needed. Concurrent users: 6, data will be cleared daily
> 💡 Note: Below is a test platform deployed with the latest code. You can burn and test if needed. Concurrent users: 6, data will be cleared daily.
```
Control Panel Address: https://2662r3426b.vicp.fun
Intelligent Control Console Address: https://2662r3426b.vicp.fun
Intelligent Control Console Address (H5): https://2662r3426b.vicp.fun/h5/index.html
Service Test Tool: https://2662r3426b.vicp.fun/test/
OTA Interface Address: https://2662r3426b.vicp.fun/xiaozhi/ota/
@@ -184,74 +203,103 @@ Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
#### 🚩 Configuration Description and Recommendations
> [!Note]
> The default configuration of this project is `Entry Level Free` settings. For better results, we recommend using `Full Streaming Configuration`.
> This project provides two configuration schemes:
>
> Since version `0.5.2`, this project supports full streaming throughout the entire lifecycle. Compared to versions before `0.5`, response speed has improved by approximately `2.5 seconds`
> 1. `Entry Level Free Settings`: Suitable for personal and home use, all components use free solutions, no additional payment required.
>
> 2. `Streaming Configuration`: Suitable for demonstrations, training, scenarios with more than 2 concurrent users, etc. Uses streaming processing technology for faster response speed and better experience.
>
> Starting from version `0.5.2`, the project supports streaming configuration. Compared to earlier versions, response speed is improved by approximately `2.5 seconds`, significantly improving user experience.
| Module Name | Entry Level Free Settings | Full Streaming Configuration |
|---------|---------|------|
| ASR(Speech Recognition) | FunASR(Local) | ✅DoubaoASR(Volcano Streaming Speech Recognition) |
| LLM(Large Language Model) | ChatGLMLLM(Zhipu glm-4-flash) | DoubaoLLM(Volcano doubao-1-5-pro-32k-250115) |
| VLLM(Vision Large Model) | ChatGLMVLLM(Zhipu glm-4v-flash) | ✅ChatGLMVLLM(Zhipu glm-4v-flash) |
| TTS(Speech Synthesis) | EdgeTTS(Microsoft Speech) | HuoshanDoubleStreamTTS(Volcano Double Streaming Speech Synthesis) |
| Intent(Intent Recognition) | function_call(Function Call) | function_call(Function Call) |
| Memory(Memory Function) | mem_local_short(Local Short-term Memory) | mem_local_short(Local Short-term Memory) |
| Module Name | Entry Level Free Settings | Streaming Configuration |
|:---:|:---:|:---:|
| ASR(Speech Recognition) | FunASR(Local) | 👍FunASRServer or 👍DoubaoStreamASR |
| LLM(Large Model) | ChatGLMLLM(Zhipu glm-4-flash) | 👍DoubaoLLM(Volcano doubao-1-5-pro-32k-250115) |
| VLLM(Vision Large Model) | ChatGLMVLLM(Zhipu glm-4v-flash) | 👍QwenVLVLLM(Qwen qwen2.5-vl-3b-instructh) |
| TTS(Speech Synthesis) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano dual-stream speech synthesis) |
| Intent(Intent Recognition) | function_call(Function calling) | function_call(Function calling) |
| Memory(Memory function) | mem_local_short(Local short-term memory) | mem_local_short(Local short-term memory) |
#### 🔧 Testing Tools
This project provides the following testing tools to help you verify the system and choose suitable models:
| Tool Name | Location | Usage Method | Function Description |
|:---:|:---|:---:|:---:|
| Audio Interaction Test Tool | main》xiaozhi-server》test》test_page.html | Open directly with Google Chrome | Tests audio playback and reception functions, verifies if Python-side audio processing is normal |
| Model Response Test Tool 1 | main》xiaozhi-server》performance_tester.py | Execute `python performance_tester.py` | Tests response speed of three core modules: ASR(speech recognition), LLM(large model), TTS(speech synthesis) |
| Model Response Test Tool 2 | main》xiaozhi-server》performance_tester_vllm.py | Execute `python performance_tester_vllm.py` | Tests VLLM(vision model) response speed |
> 💡 Note: When testing model speed, only models with configured keys will be tested.
---
## Feature List ✨
### Implemented ✅
![请参考-全模块安装架构图](docs/images/deploy2.png)
| Feature Module | Description |
|---------|------|
| Communication Protocol | Based on `xiaozhi-esp32` protocol, implements data interaction through WebSocket |
| Dialogue Interaction | Supports wake-up dialogue, manual dialogue, and real-time interruption. Auto-sleep after long periods of no dialogue |
| Intent Recognition | Supports LLM intent recognition, function call, reducing hard-coded intent judgment |
| Multi-language Recognition | Supports Mandarin, Cantonese, English, Japanese, Korean (default using FunASR) |
| LLM Module | Supports flexible LLM module switching, default using ChatGLMLLM, can also use Ali Bailian, DeepSeek, Ollama, etc. |
| TTS Module | Supports EdgeTTS (default), Volcano Engine Doubao TTS, and other TTS interfaces |
| Memory Function | Supports ultra-long memory, local summary memory, and no memory modes |
| IOT Function | Supports managing registered device IOT functionality, supports smart IoT control based on dialogue context |
| Control Panel | Provides Web management interface, supports agent management, user management, system configuration, etc. |
|:---:|:---|
| Core Architecture | Based on WebSocket and HTTP servers, provides complete console management and authentication system |
| Voice Interaction | Supports streaming ASR(speech recognition), streaming TTS(speech synthesis), VAD(voice activity detection), supports multi-language recognition and voice processing |
| Voiceprint Recognition | Supports multi-user voiceprint registration, management, and recognition, processes in parallel with ASR, real-time speaker identity recognition and passes to LLM for personalized responses |
| Intelligent Dialogue | Supports multiple LLM(large language models), implements intelligent dialogue |
| Visual Perception | Supports multiple VLLM(vision large models), implements multimodal interaction |
| Intent Recognition | Supports LLM intent recognition, Function Call function calling, provides plugin-based intent processing mechanism |
| Memory System | Supports local short-term memory, mem0ai interface memory, with memory summarization functionality |
| Tool Calling | Supports client IOT protocol, client MCP protocol, server MCP protocol, MCP endpoint protocol, custom tool functions |
| Management Backend | Provides Web management interface, supports user management, system configuration, and device management |
| Testing Tools | Provides performance testing tools, vision model testing tools, and audio interaction testing tools |
| Deployment Support | Supports Docker deployment and local deployment, provides complete configuration file management |
| Plugin System | Supports functional plugin extensions, custom plugin development, and plugin hot-loading |
### In Development 🚧
### Under Development 🚧
To learn about specific development progress, [click here](https://github.com/users/xinnan-tech/projects/3)
To learn about specific development plan progress, [click here](https://github.com/users/xinnan-tech/projects/3)
If you are a software developer, here is an [Open Letter to Developers](docs/contributor_open_letter.md). Welcome to join!
---
## Product Ecosystem 👬
Xiaozhi is an ecosystem. When using this product, you might also want to check out other excellent projects in this ecosystem
Xiaozhi is an ecosystem. When using this product, you can also check out other excellent projects in this ecosystem
| Project Name | Project Address | Project Description |
|:---------------------|:--------|:--------|
| Xiaozhi Android Client | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | A Flutter-based Android and iOS voice dialogue application supporting real-time voice interaction and text dialogue. |
| Xiaozhi PC Client | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | This project provides a Python-based Xiaozhi AI client, allowing you to experience Xiaozhi AI's functionality through code even without physical hardware. |
| Xiaozhi Java Server | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | The Java version of Xiaozhi open-source backend service is a Java-based open-source project.<br/>It includes both frontend and backend services, aiming to provide users with a complete backend service solution. |
| Xiaozhi Android Client | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | An Android and iOS voice dialogue application based on xiaozhi-server, supporting real-time voice interaction and text dialogue.<br/>Currently a Flutter version, connecting iOS and Android platforms. |
| Xiaozhi Desktop Client | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | This project provides a Python-based AI client for beginners, allowing users to experience Xiaozhi AI functionality through code even without physical hardware conditions. |
| Xiaozhi Java Server | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | Xiaozhi open-source backend service Java version is a Java-based open-source project.<br/>It includes frontend and backend services, aiming to provide users with a complete backend service solution. |
---
## Supported Platforms/Components List 📋
### LLM Language Models
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| openai interface call | Ali Bailian, Volcano Engine Doubao, DeepSeek, Zhipu ChatGLM, Gemini | Zhipu ChatGLM, Gemini |
| ollama interface call | Ollama | - |
| dify interface call | Dify | - |
| fastgpt interface call | Fastgpt | - |
| coze interface call | Coze | - |
| OpenAI interface calls | Alibaba Bailian, Volcano Engine Doubao, DeepSeek, Zhipu ChatGLM, Gemini | Zhipu ChatGLM, Gemini |
| Ollama interface calls | Ollama | - |
| Dify interface calls | Dify | - |
| FastGPT interface calls | FastGPT | - |
| Coze interface calls | Coze | - |
In fact, any LLM that supports openai interface calls can be integrated and used.
In fact, any LLM that supports OpenAI interface calls can be integrated and used.
---
### VLLM Vision Models
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| OpenAI interface calls | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
In fact, any VLLM that supports OpenAI interface calls can be integrated and used.
---
### TTS Speech Synthesis
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| API Call | EdgeTTS, Volcano Engine Doubao TTS, Tencent Cloud, Alibaba Cloud TTS, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS | EdgeTTS, CosyVoiceSiliconflow(partial) |
| Local Service | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS |
| Interface calls | EdgeTTS, Volcano Engine Doubao TTS, Tencent Cloud, Alibaba Cloud TTS, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(partial) |
| Local services | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS |
---
@@ -259,7 +307,7 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:---:|:---------:|:----:|:----:|:--:|
| VAD | SileroVAD | Local Usage | Free | |
| VAD | SileroVAD | Local use | Free | |
---
@@ -267,8 +315,16 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| Local Usage | FunASR, SherpaASR | FunASR, SherpaASR |
| API Call | DoubaoASR, FunASRServer, TencentASR, AliyunASR | FunASRServer |
| Local use | FunASR, SherpaASR | FunASR, SherpaASR |
| Interface calls | DoubaoASR, FunASRServer, TencentASR, AliyunASR | FunASRServer |
---
### Voiceprint Recognition
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| Local use | 3D-Speaker | 3D-Speaker |
---
@@ -276,8 +332,8 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:------:|:---------------:|:----:|:---------:|:--:|
| Memory | mem0ai | API Call | 1000 calls/month quota | |
| Memory | mem_local_short | Local Summary | Free | |
| Memory | mem0ai | Interface calls | 1000 times/month quota | |
| Memory | mem_local_short | Local summarization | Free | |
---
@@ -285,8 +341,8 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:------:|:-------------:|:----:|:-------:|:---------------------:|
| Intent | intent_llm | API Call | Based on LLM pricing | Uses large model for intent recognition, highly versatile |
| Intent | function_call | API Call | Based on LLM pricing | Uses large model function calls for intent, fast and effective |
| Intent | intent_llm | Interface calls | Based on LLM pricing | Recognizes intent through large models, strong generalization |
| Intent | function_call | Interface calls | Based on LLM pricing | Completes intent through large model function calling, fast speed, good effect |
---
@@ -294,11 +350,13 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Logo | Project/Company | Description |
|:---:|:---:|:---|
| <img src="./docs/images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | This project was inspired by [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) and implemented based on it |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Thanks to [Tenclass](https://www.tenclass.com/) for establishing standard communication protocols, multi-device compatibility solutions, and high-concurrency scenario practices for the Xiaozhi ecosystem; providing full-chain technical documentation support for this project |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Thanks to [Xuanfeng Technology](https://github.com/Eric0308) for contributing the function call framework, MCP communication protocol, and plugin call mechanism implementation code, significantly improving front-end device (IoT) interaction efficiency and functional extensibility through standardized instruction scheduling system and dynamic expansion capabilities |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Thanks to [Huiyuan Design](http://ui.kwd988.net/) for providing professional visual solutions for this project, empowering the product user experience with their design experience serving over a thousand enterprises |
| <img src="./docs/images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Thanks to [Xi'an Qinren Information Technology](https://www.029app.com/) for deepening the visual system of this project, ensuring consistency and extensibility of the overall design style in multi-scenario applications |
| <img src="./docs/images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | This project is inspired by [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) and implemented on its basis |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Thanks to [Tenclass](https://www.tenclass.com/) for formulating standard communication protocols, multi-device compatibility solutions, and high-concurrency scenario practice demonstrations for the Xiaozhi ecosystem; providing full-link technical documentation support for this project |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Thanks to [Xuanfeng Technology](https://github.com/Eric0308) for contributing function calling framework, MCP communication protocol, and plugin-based calling mechanism implementation code. Through standardized instruction scheduling system and dynamic expansion capabilities, it significantly improves the interaction efficiency and functional extensibility of frontend devices (IoT) |
| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Thanks to [huangjunsen](https://github.com/huangjunsen0406) for contributing the `Smart Control Console Mobile` module, which enables efficient control and real-time interaction across mobile devices, significantly enhancing the system's operational convenience and management efficiency in mobile scenarios. |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Thanks to [Huiyuan Design](http://ui.kwd988.net/) for providing professional visual solutions for this project, using their design practical experience serving over a thousand enterprises to empower this project's product user experience |
| <img src="./docs/images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Thanks to [Xi'an Qinren Information Technology](https://www.029app.com/) for deepening this project's visual system, ensuring consistency and extensibility of overall design style in multi-scenario applications |
| <img src="./docs/images/logo_contributors.png" width="160"> | [Code Contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Thanks to [all code contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), your efforts have made the project more robust and powerful. |
<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+405 -97
View File
@@ -1,105 +1,413 @@
#!/bin/sh
# 脚本作者@VanillaNahida
# 本文件是用于一键自动下载本项目所需文件,自动创建好目录
# 所需条件(否则无法使用):
# 1、请确保你的环境可以正常访问 GitHub 否则无法下载脚本
#
# 检测操作系统类型
case "$(uname -s)" in
Linux*) OS=Linux;;
Darwin*) OS=Mac;;
CYGWIN*) OS=Windows;;
MINGW*) OS=Windows;;
MSYS*) OS=Windows;;
*) OS=UNKNOWN;;
# 暂且只支持X86版本的Ubuntu系统,其他系统未测试
# 定义中断处理函数
handle_interrupt() {
echo ""
echo "安装已被用户中断(Ctrl+C或Esc)"
echo "如需重新安装,请再次运行脚本"
exit 1
}
# 设置信号捕获,处理Ctrl+C
trap handle_interrupt SIGINT
# 处理Esc键
# 保存终端设置
old_stty_settings=$(stty -g)
# 设置终端立即响应,不回显
stty -icanon -echo min 1 time 0
# 后台进程检测Esc键
(while true; do
read -r key
if [[ $key == $'\e' ]]; then
# 检测到Esc键,触发中断处理
kill -SIGINT $$
break
fi
done) &
# 脚本结束时恢复终端设置
trap 'stty "$old_stty_settings"' EXIT
# 打印彩色字符画
echo -e "\e[1;32m" # 设置颜色为亮绿色
cat << "EOF"
脚本作者:@Bilibili 香草味的纳西妲喵
__ __ _ _ _ _ _ _ _ _
\ \ / / (_)| || | | \ | | | | (_) | |
\ \ / /__ _ _ __ _ | || | __ _ | \| | __ _ | |__ _ __| | __ _
\ \/ // _` || '_ \ | || || | / _` | | . ` | / _` || '_ \ | | / _` | / _` |
\ /| (_| || | | || || || || (_| | | |\ || (_| || | | || || (_| || (_| |
\/ \__,_||_| |_||_||_||_| \__,_| |_| \_| \__,_||_| |_||_| \__,_| \__,_|
EOF
echo -e "\e[0m" # 重置颜色
echo -e "\e[1;36m 小智服务端全量部署一键安装脚本 Ver 0.2 2025年8月20日更新 \e[0m\n"
sleep 1
# 检查并安装whiptail
check_whiptail() {
if ! command -v whiptail &> /dev/null; then
echo "正在安装whiptail..."
apt update
apt install -y whiptail
fi
}
check_whiptail
# 创建确认对话框
whiptail --title "安装确认" --yesno "即将安装小智服务端,是否继续?" \
--yes-button "继续" --no-button "退出" 10 50
# 根据用户选择执行操作
case $? in
0)
;;
1)
exit 1
;;
esac
# 设置颜色(Windows CMD 不支持,但不影响使用)
if [ "$OS" = "Windows" ]; then
GREEN=""
RED=""
NC=""
else
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
fi
echo "${GREEN}开始安装小智服务端...${NC}"
# 创建必要的目录
echo "创建目录结构..."
mkdir -p xiaozhi-server/data xiaozhi-server/models/SenseVoiceSmall
cd xiaozhi-server || exit
# 根据操作系统选择下载命令
if [ "$OS" = "Windows" ]; then
DOWNLOAD_CMD="curl -L -o"
if ! command -v curl >/dev/null 2>&1; then
DOWNLOAD_CMD="powershell -Command Invoke-WebRequest -Uri"
DOWNLOAD_CMD_SUFFIX="-OutFile"
fi
else
if command -v curl >/dev/null 2>&1; then
DOWNLOAD_CMD="curl -L -o"
elif command -v wget >/dev/null 2>&1; then
DOWNLOAD_CMD="wget -O"
else
echo "${RED}错误: 需要安装 curl 或 wget${NC}"
exit 1
fi
fi
# 下载语音识别模型
echo "下载语音识别模型..."
if [ "$DOWNLOAD_CMD" = "powershell -Command Invoke-WebRequest -Uri" ]; then
$DOWNLOAD_CMD "https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt" $DOWNLOAD_CMD_SUFFIX "models/SenseVoiceSmall/model.pt"
else
$DOWNLOAD_CMD "models/SenseVoiceSmall/model.pt" "https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt"
fi
if [ $? -ne 0 ]; then
echo "${RED}模型下载失败。请手动从以下地址下载:${NC}"
echo "1. https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt"
echo "2. 百度网盘: https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg (提取码: qvna)"
echo "下载后请将文件放置在 models/SenseVoiceSmall/model.pt"
fi
# 下载配置文件
echo "下载配置文件..."
if [ "$DOWNLOAD_CMD" = "powershell -Command Invoke-WebRequest -Uri" ]; then
$DOWNLOAD_CMD "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/docker-compose.yml" $DOWNLOAD_CMD_SUFFIX "docker-compose.yml"
$DOWNLOAD_CMD "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/config.yaml" $DOWNLOAD_CMD_SUFFIX "data/.config.yaml"
else
$DOWNLOAD_CMD "docker-compose.yml" "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/docker-compose.yml"
$DOWNLOAD_CMD "data/.config.yaml" "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/config.yaml"
fi
# 检查文件是否存在
echo "检查文件完整性..."
FILES_TO_CHECK="docker-compose.yml data/.config.yaml models/SenseVoiceSmall/model.pt"
ALL_FILES_EXIST=true
for FILE in $FILES_TO_CHECK; do
if [ ! -f "$FILE" ]; then
echo "${RED}错误: $FILE 不存在${NC}"
ALL_FILES_EXIST=false
fi
done
if [ "$ALL_FILES_EXIST" = false ]; then
echo "${RED}某些文件下载失败,请检查上述错误信息并手动下载缺失的文件。${NC}"
# 检查root权限
if [ $EUID -ne 0 ]; then
whiptail --title "权限错误" --msgbox "请使用root权限运行本脚本" 10 50
exit 1
fi
echo "${GREEN}文件下载完成!${NC}"
echo "请编辑 data/.config.yaml 文件配置你的API密钥。"
echo "配置完成后,运行以下命令启动服务:"
echo "${GREEN}docker-compose up -d${NC}"
echo "查看日志请运行:"
echo "${GREEN}docker logs -f xiaozhi-esp32-server${NC}"
# 检查系统版本
if [ -f /etc/os-release ]; then
. /etc/os-release
if [ "$ID" != "debian" ] && [ "$ID" != "ubuntu" ]; then
whiptail --title "系统错误" --msgbox "该脚本只支持Debian/Ubuntu系统执行" 10 60
exit 1
fi
else
whiptail --title "系统错误" --msgbox "无法确定系统版本,该脚本只支持Debian/Ubuntu系统执行" 10 60
exit 1
fi
# 提示用户编辑配置文件
echo "\n${RED}重要提示:${NC}"
echo "1. 请确保编辑 data/.config.yaml 文件,配置必要的API密钥"
echo "2. 特别是 ChatGLM 和 mem0ai 的密钥必须配置"
echo "3. 配置完成后再启动 docker 服务"
# 下载配置文件函数
check_and_download() {
local filepath=$1
local url=$2
if [ ! -f "$filepath" ]; then
if ! curl -fL --progress-bar "$url" -o "$filepath"; then
whiptail --title "错误" --msgbox "${filepath}文件下载失败" 10 50
exit 1
fi
else
echo "${filepath}文件已存在,跳过下载"
fi
}
# 检查是否已安装
check_installed() {
# 检查目录是否存在且非空
if [ -d "/opt/xiaozhi-server/" ] && [ "$(ls -A /opt/xiaozhi-server/)" ]; then
DIR_CHECK=1
else
DIR_CHECK=0
fi
# 检查容器是否存在
if docker inspect xiaozhi-esp32-server > /dev/null 2>&1; then
CONTAINER_CHECK=1
else
CONTAINER_CHECK=0
fi
# 两次检查都通过
if [ $DIR_CHECK -eq 1 ] && [ $CONTAINER_CHECK -eq 1 ]; then
return 0 # 已安装
else
return 1 # 未安装
fi
}
# 更新相关
if check_installed; then
if whiptail --title "已安装检测" --yesno "检测到小智服务端已安装,是否进行升级?" 10 60; then
# 用户选择升级,执行清理操作
echo "开始升级操作..."
# 停止并移除所有docker-compose服务
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml down
# 停止并删除特定容器(考虑容器可能不存在的情况)
containers=(
"xiaozhi-esp32-server"
"xiaozhi-esp32-server-web"
"xiaozhi-esp32-server-db"
"xiaozhi-esp32-server-redis"
)
for container in "${containers[@]}"; do
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
docker stop "$container" >/dev/null 2>&1 && \
docker rm "$container" >/dev/null 2>&1 && \
echo "成功移除容器: $container"
else
echo "容器不存在,跳过: $container"
fi
done
# 删除特定镜像(考虑镜像可能不存在的情况)
images=(
"ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest"
"ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest"
)
for image in "${images[@]}"; do
if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${image}$"; then
docker rmi "$image" >/dev/null 2>&1 && \
echo "成功删除镜像: $image"
else
echo "镜像不存在,跳过: $image"
fi
done
echo "所有清理操作完成"
# 备份原有配置文件
mkdir -p /opt/xiaozhi-server/backup/
if [ -f /opt/xiaozhi-server/data/.config.yaml ]; then
cp /opt/xiaozhi-server/data/.config.yaml /opt/xiaozhi-server/backup/.config.yaml
echo "已备份原有配置文件到 /opt/xiaozhi-server/backup/.config.yaml"
fi
# 下载最新版配置文件
check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
# 启动Docker服务
echo "开始启动最新版本服务..."
# 升级完成后标记,跳过后续下载步骤
UPGRADE_COMPLETED=1
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
else
whiptail --title "跳过升级" --msgbox "已取消升级,将继续使用当前版本。" 10 50
# 跳过升级,继续执行后续安装流程
fi
fi
# 检查curl安装
if ! command -v curl &> /dev/null; then
echo "------------------------------------------------------------"
echo "未检测到curl,正在安装..."
apt update
apt install -y curl
else
echo "------------------------------------------------------------"
echo "curl已安装,跳过安装步骤"
fi
# 检查Docker安装
if ! command -v docker &> /dev/null; then
echo "------------------------------------------------------------"
echo "未检测到Docker,正在安装..."
# 使用国内镜像源替代官方源
DISTRO=$(lsb_release -cs)
MIRROR_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu"
GPG_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg"
# 安装基础依赖
apt update
apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg
# 创建密钥目录并添加国内镜像源密钥
mkdir -p /etc/apt/keyrings
curl -fsSL "$GPG_URL" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加国内镜像源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $MIRROR_URL $DISTRO stable" \
> /etc/apt/sources.list.d/docker.list
# 添加备用官方源密钥(避免国内源密钥验证失败)
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8 2>/dev/null || \
echo "警告:部分密钥添加失败,继续尝试安装..."
# 安装Docker
apt update
apt install -y docker-ce docker-ce-cli containerd.io
# 启动服务
systemctl start docker
systemctl enable docker
# 检查是否安装成功
if docker --version; then
echo "------------------------------------------------------------"
echo "Docker安装完成!"
else
whiptail --title "错误" --msgbox "Docker安装失败,请检查日志。" 10 50
exit 1
fi
else
echo "Docker已安装,跳过安装步骤"
fi
# Docker镜像源配置
MIRROR_OPTIONS=(
"1" "轩辕镜像 (推荐)"
"2" "腾讯云镜像源"
"3" "中科大镜像源"
"4" "网易163镜像源"
"5" "华为云镜像源"
"6" "阿里云镜像源"
"7" "自定义镜像源"
"8" "跳过配置"
)
MIRROR_CHOICE=$(whiptail --title "选择Docker镜像源" --menu "请选择要使用的Docker镜像源" 20 60 10 \
"${MIRROR_OPTIONS[@]}" 3>&1 1>&2 2>&3) || {
echo "用户取消选择,退出脚本"
exit 1
}
case $MIRROR_CHOICE in
1) MIRROR_URL="https://docker.xuanyuan.me" ;;
2) MIRROR_URL="https://mirror.ccs.tencentyun.com" ;;
3) MIRROR_URL="https://docker.mirrors.ustc.edu.cn" ;;
4) MIRROR_URL="https://hub-mirror.c.163.com" ;;
5) MIRROR_URL="https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com" ;;
6) MIRROR_URL="https://registry.aliyuncs.com" ;;
7) MIRROR_URL=$(whiptail --title "自定义镜像源" --inputbox "请输入完整的镜像源URL:" 10 60 3>&1 1>&2 2>&3) ;;
8) MIRROR_URL="" ;;
esac
if [ -n "$MIRROR_URL" ]; then
mkdir -p /etc/docker
if [ -f /etc/docker/daemon.json ]; then
cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
fi
cat > /etc/docker/daemon.json <<EOF
{
"dns": ["8.8.8.8", "114.114.114.114"],
"registry-mirrors": ["$MIRROR_URL"]
}
EOF
whiptail --title "配置成功" --msgbox "已成功添加镜像源: $MIRROR_URL\n请按Enter键重启Docker服务并继续..." 12 60
echo "------------------------------------------------------------"
echo "开始重启Docker服务..."
systemctl restart docker.service
fi
# 创建安装目录
echo "------------------------------------------------------------"
echo "开始创建安装目录..."
# 检查并创建数据目录
if [ ! -d /opt/xiaozhi-server/data ]; then
mkdir -p /opt/xiaozhi-server/data
echo "已创建数据目录: /opt/xiaozhi-server/data"
else
echo "目录xiaozhi-server/data已存在,跳过创建"
fi
# 检查并创建模型目录
if [ ! -d /opt/xiaozhi-server/models/SenseVoiceSmall ]; then
mkdir -p /opt/xiaozhi-server/models/SenseVoiceSmall
echo "已创建模型目录: /opt/xiaozhi-server/models/SenseVoiceSmall"
else
echo "目录xiaozhi-server/models/SenseVoiceSmall已存在,跳过创建"
fi
echo "------------------------------------------------------------"
echo "开始下载语音识别模型"
# 下载模型文件
MODEL_PATH="/opt/xiaozhi-server/models/SenseVoiceSmall/model.pt"
if [ ! -f "$MODEL_PATH" ]; then
(
for i in {1..20}; do
echo $((i*5))
sleep 0.5
done
) | whiptail --title "下载中" --gauge "开始下载语音识别模型..." 10 60 0
curl -fL --progress-bar https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt -o "$MODEL_PATH" || {
whiptail --title "错误" --msgbox "model.pt文件下载失败" 10 50
exit 1
}
else
echo "model.pt文件已存在,跳过下载"
fi
# 如果不是升级完成,才执行下载
if [ -z "$UPGRADE_COMPLETED" ]; then
check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
fi
# 启动Docker服务
(
echo "------------------------------------------------------------"
echo "正在拉取Docker镜像..."
echo "这可能需要几分钟时间,请耐心等待"
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
if [ $? -ne 0 ]; then
whiptail --title "错误" --msgbox "Docker服务启动失败,请尝试更换镜像源后重新执行本脚本" 10 60
exit 1
fi
echo "------------------------------------------------------------"
echo "正在检查服务启动状态..."
TIMEOUT=300
START_TIME=$(date +%s)
while true; do
CURRENT_TIME=$(date +%s)
if [ $((CURRENT_TIME - START_TIME)) -gt $TIMEOUT ]; then
whiptail --title "错误" --msgbox "服务启动超时,未在指定时间内找到预期日志内容" 10 60
exit 1
fi
if docker logs xiaozhi-esp32-server-web 2>&1 | grep -q "Started AdminApplication in"; then
break
fi
sleep 1
done
echo "服务端启动成功!正在完成配置..."
echo "正在启动服务..."
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
echo "服务启动完成!"
)
# 密钥配置
# 获取服务器公网地址
PUBLIC_IP=$(hostname -I | awk '{print $1}')
whiptail --title "配置服务器密钥" --msgbox "请使用浏览器,访问下方链接,打开智控台并注册账号: \n\n内网地址:http://127.0.0.1:8002/\n公网地址:http://$PUBLIC_IP:8002/ (若是云服务器请在服务器安全组放行端口 8000 8001 8002)。\n\n注册的第一个用户即是超级管理员,以后注册的用户都是普通用户。普通用户只能绑定设备和配置智能体; 超级管理员可以进行模型管理、用户管理、参数配置等功能。\n\n注册好后请按Enter键继续" 18 70
SECRET_KEY=$(whiptail --title "配置服务器密钥" --inputbox "请使用超级管理员账号登录智控台\n内网地址:http://127.0.0.1:8002/\n公网地址:http://$PUBLIC_IP:8002/\n在顶部菜单 参数字典 → 参数管理 找到参数编码: server.secret (服务器密钥) \n复制该参数值并输入到下面输入框\n\n请输入密钥(留空则跳过配置):" 15 60 3>&1 1>&2 2>&3)
if [ -n "$SECRET_KEY" ]; then
python3 -c "
import sys, yaml;
config_path = '/opt/xiaozhi-server/data/.config.yaml';
with open(config_path, 'r') as f:
config = yaml.safe_load(f) or {};
config['manager-api'] = {'url': 'http://xiaozhi-esp32-server-web:8002/xiaozhi', 'secret': '$SECRET_KEY'};
with open(config_path, 'w') as f:
yaml.dump(config, f);
"
docker restart xiaozhi-esp32-server
fi
# 获取并显示地址信息
LOCAL_IP=$(hostname -I | awk '{print $1}')
# 修复日志文件获取不到ws的问题,改为硬编码
whiptail --title "安装完成!" --msgbox "\
服务端相关地址如下:\n\
管理后台访问地址: http://$LOCAL_IP:8002\n\
OTA 地址: http://$LOCAL_IP:8002/xiaozhi/ota/\n\
视觉分析接口地址: http://$LOCAL_IP:8003/mcp/vision/explain\n\
WebSocket 地址: ws://$LOCAL_IP:8000/xiaozhi/v1/\n\
\n安装完毕!感谢您的使用!\n按Enter键退出..." 16 70
+34 -57
View File
@@ -8,47 +8,13 @@ docker镜像已支持x86架构、arm64架构的CPU,支持在国产操作系统
如果您的电脑还没安装docker,可以按照这里的教程安装:[docker安装](https://www.runoob.com/docker/ubuntu-docker-install.html)
如果你已经安装好docker,你可以[1.1使用懒人脚本](#11-懒人脚本)自动帮你下载所需的文件和配置文件,你可以使用docker[1.2手动部署](#12-手动部署)
安装好docker后,进继续
### 1.1 懒人脚本
### 1.1 手动部署
你可以使用以下命令一键下载并执行部署脚本:
请确保你的环境可以正常访问 GitHub 否则无法下载脚本。
```bash
curl -L -o docker-setup.sh https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/docker-setup.sh
```
#### 1.1.1 创建目录
如果您的电脑是windows系统,请使用使用 Git Bash、WSL、PowerShell 或 CMD 运行以下命令:
```bash
# Git Bash 或 WSL
sh docker-setup.sh
# PowerShell 或 CMD
.\docker-setup.sh
```
如果您的电脑是linux 或者 macos 系统,请使用终端运行以下命令:
```bash
chmod +x docker-setup.sh
./docker-setup.sh
```
脚本会自动完成以下操作:
> 1. 创建必要的目录结构
> 2. 下载语音识别模型
> 3. 下载配置文件
> 4. 检查文件完整性
>
> 执行完成后,请按照提示配置 API 密钥。
当你一切顺利完成以上操作后,继续操作[配置项目文件](#2-配置项目文件)
### 1.2 手动部署
如果懒人脚本无法正常运行,请按本章节1.2进行手动部署。
#### 1.2.1 创建目录
安装完后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
安装完docker后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
创建好目录后,你需要在`xiaozhi-server`下面创建`data`文件夹和`models`文件夹,`models`下面还要再创建`SenseVoiceSmall`文件夹。
@@ -61,18 +27,18 @@ xiaozhi-server
├─ SenseVoiceSmall
```
#### 1.2.2 下载语音识别模型文件
#### 1.1.2 下载语音识别模型文件
你需要下载语音识别的模型文件,因为本项目的默认语音识别用的是本地离线语音识别方案。可通过这个方式下载
[跳转到下载语音识别模型文件](#模型文件)
下载完后,回到本教程。
#### 1.2.3 下载配置文件
#### 1.1.3 下载配置文件
你需要下载两个配置文件:`docker-compose.yaml``config.yaml`。需要从项目仓库下载这两个文件。
##### 1.2.3.1 下载 docker-compose.yaml
##### 1.1.3.1 下载 docker-compose.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/docker-compose.yml)。
@@ -81,7 +47,7 @@ xiaozhi-server
下载完后,回到本教程继续往下。
##### 1.2.3.2 创建 config.yaml
##### 1.1.3.2 创建 config.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/config.yaml)。
@@ -260,7 +226,7 @@ LLM:
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
@@ -290,19 +256,30 @@ LLM:
2、 [基于虾哥编译好的固件配置自定义服务器](firmware-setting.md)了。
# 常见问题
以下是一些常见问题,供参考:
[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)
[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)
[3、TTS 经常失败,经常超时](./FAQ.md)
[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)
[5、如何提高小智对话响应速度?](./FAQ.md)
[6、我说话很慢,停顿时小智老是抢话](./FAQ.md)
[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md)
1、[为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)<br/>
2、[为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)<br/>
3、[TTS 经常失败,经常超时](./FAQ.md)<br/>
4、[使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)<br/>
5、[如何提高小智对话响应速度?](./FAQ.md)<br/>
6、[我说话很慢,停顿时小智老是抢话](./FAQ.md)<br/>
## 部署相关教程
1、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
2、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
## 拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
10、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
## 语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
## 性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
+61 -23
View File
@@ -7,7 +7,34 @@ docker镜像已支持x86架构、arm64架构的CPU,支持在国产操作系统
如果您的电脑还没安装docker,可以按照这里的教程安装:[docker安装](https://www.runoob.com/docker/ubuntu-docker-install.html)
#### 1.1 创建目录
docker 安装全模块有两种方式,你可以[使用懒人脚本](./Deployment_all.md#11-懒人脚本)(作者[@VanillaNahida](https://github.com/VanillaNahida)
脚本会自动帮你下载所需的文件和配置文件,你也可以使用[手动部署](./Deployment_all.md#12-手动部署)从零搭建。
### 1.1 懒人脚本
部署简便,可以参考[视频教程](https://www.bilibili.com/video/BV17bbvzHExd/) ,文字版教程如下:
> [!NOTE]
> 暂且只支持Ubuntu服务器一键部署,其他系统未尝试,可能会有一些奇怪的bug
使用SSH工具连接到服务器,以root权限执行如下脚本
```bash
sudo bash -c "$(wget -qO- https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/docker-setup.sh)"
```
脚本会自动完成以下操作:
> 1. 安装Docker
> 2. 配置镜像源
> 3. 下载/拉取镜像
> 4. 下载语音识别模型文件
> 5. 引导配置服务端
>
执行完成后简单配置后,再参照[4. 运行程序](#4. 运行程序)和[5.重启xiaozhi-esp32-server](#5.重启xiaozhi-esp32-server)里提到的最重要的3件事情,完成3这三项配置后即可使用。
### 1.2 手动部署
#### 1.2.1 创建目录
安装完后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
@@ -22,22 +49,22 @@ xiaozhi-server
├─ SenseVoiceSmall
```
#### 1.2 下载语音识别模型文件
#### 1.2.2 下载语音识别模型文件
本项目语音识别模型,默认使用`SenseVoiceSmall`模型,进行语音转文字。因为模型较大,需要独立下载,下载后把`model.pt`
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
#### 1.3 下载配置文件
#### 1.2.3 下载配置文件
你需要下载两个配置文件:`docker-compose_all.yaml``config_from_api.yaml`。需要从项目仓库下载这两个文件。
##### 1.3.1 下载 docker-compose_all.yaml
##### 1.2.3.1 下载 docker-compose_all.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/docker-compose_all.yml)。
@@ -48,7 +75,7 @@ xiaozhi-server
下载完后,回到本教程继续往下。
##### 1.3.2 下载 config_from_api.yaml
##### 1.2.3.2 下载 config_from_api.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/config_from_api.yaml)。
@@ -179,12 +206,12 @@ docker logs -f xiaozhi-esp32-server
OTA接口:
```
http://你电脑局域网的ip:8002/xiaozhi/ota/
http://你宿主机局域网的ip:8002/xiaozhi/ota/
```
Websocket接口:
```
ws://你电脑局域网的ip:8000/xiaozhi/v1/
ws://你宿主机的ip:8000/xiaozhi/v1/
```
### 第三件重要的事情
@@ -358,7 +385,7 @@ pip install -r requirements.txt
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
@@ -431,19 +458,30 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/
2、 [基于虾哥编译好的固件配置自定义服务器](firmware-setting.md)了。
# 常见问题
以下是一些常见问题,供参考:
[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)
[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)
[3、TTS 经常失败,经常超时](./FAQ.md)
[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)
[5、如何提高小智对话响应速度?](./FAQ.md)
[6、我说话很慢,停顿时小智老是抢话](./FAQ.md)
[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md)
1、[为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)<br/>
2、[为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)<br/>
3、[TTS 经常失败,经常超时](./FAQ.md)<br/>
4、[使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)<br/>
5、[如何提高小智对话响应速度?](./FAQ.md)<br/>
6、[我说话很慢,停顿时小智老是抢话](./FAQ.md)<br/>
## 部署相关教程
1、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
2、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
## 拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
7、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
8、[天气插件使用指南](./weather-integration.md)<br/>
## 语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
## 性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
+36 -62
View File
@@ -34,59 +34,18 @@ conda install conda-forge::ffmpeg
### 5、如何提高小智对话响应速度? ⚡
本项目默认配置为低成本方案,建议初学者先使用默认免费模型,解决"跑得动"的问题,再优化"跑得快"。
如需提升响应速度,可尝试更换各组件。以下为各组件的响应速度测试数据(仅供参考,不构成承诺):
如需提升响应速度,可尝试更换各组件。`0.5.2`版本起,项目支持流式配置,相比早期版本,响应速度提升约`2.5秒`,显著改善用户体验。
| 影响因素 | 因素值 |
|:-----:|:----------------:|
| 测试地点 | 广东省广州市海珠区 |
| 测试时间 | 2025年2月19日 12:52 |
| 宽带运营商 | 中国联通 |
| 模块名称 | 入门全免费设置 | 流式配置 |
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
测试方法:
1、把各组件的密钥配置上去,只有配置了密钥的组件才参与测试。
2、配置完密钥后,执行以下方法
```
# 进入项目根目录,执行以下命令:
conda activate xiaozhi-esp32-server
python performance_tester.py
```
生成报告如下
LLM 性能排行:
| 模块名称 | 平均首Token时间 | 平均总响应时间 |
|:-----------|:-----------|:--------|
| AliLLM | 0.547s | 1.485s |
| ChatGLMLLM | 0.677s | 3.057s |
TTS 性能排行:
| 模块名称 | 平均合成时间 |
|----------------------|--------|
| EdgeTTS | 1.019s |
| DoubaoTTS | 0.503s |
| CosyVoiceSiliconflow | 3.732s |
推荐配置组合 (综合响应速度):
| 组合方案 | 综合得分 | LLM首Token | TTS合成 |
|-------------------------------|-------|-----------|--------|
| AliLLM + DoubaoTTS | 0.539 | 0.547s | 0.503s |
| AliLLM + EdgeTTS | 0.642 | 0.547s | 1.019s |
| ChatGLMLLM + DoubaoTTS | 0.642 | 0.677s | 0.503s |
| ChatGLMLLM + EdgeTTS | 0.745 | 0.677s | 1.019s |
| AliLLM + CosyVoiceSiliconflow | 1.184 | 0.547s | 3.732s |
### 结论 🔍
`2025年2月19日`,如果我的电脑在`广东省广州市海珠区`,且使用的是`中国联通`网络,我会优先使用:
- LLM`AliLLM`
- TTS`DoubaoTTS`
如果您关心各组件的耗时,请查阅[小智各组件性能测试报告](https://github.com/xinnan-tech/xiaozhi-performance-research),可按报告中的测试方法在您的环境中实际测试。
### 6、我说话很慢,停顿时小智老是抢话 🗣️
@@ -100,20 +59,35 @@ VAD:
min_silence_duration_ms: 700 # 如果说话停顿较长,可将此值调大
```
### 7、我想通过小智控制电灯、空调、远程开关机等操作 💡
### 7、部署相关教程
1、[如何进行最简化部署](./Deployment.md)<br/>
2、[如何进行全模块部署](./Deployment_all.md)<br/>
2、[如何部署MQTT网关开启MQTT+UDP协议](./mqtt-gateway-integration.md)<br/>
3、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
4、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
参考教程[ESP32设备与HomeAssistant集成指南](./homeassistant-integration.md)
### 8、编译固件相关教程
1、[如何自己编译小智固件](./firmware-build.md)<br/>
2、[如何基于虾哥编译好的固件修改OTA地址](./firmware-setting.md)<br/>
### 8、如何开启手机注册智控台 📱
### 8、拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
10、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
参考教程[阿里云短信集成指南](./ali-sms-integration.md)
### 9、语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
### 9、如何开启视觉模型实现拍照识物 📷
### 10、性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
参考教程[视觉模型使用指南](./mcp-vision-integration.md)
### 13、更多问题,可联系我们反馈 💬
### 10、更多问题,可联系我们反馈 💬
可以在[issues](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues)提交您的问题。
也可以发邮件我们取得联系:huangrongzhuang@xin-nan.com
可以在[issues](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues)提交您的问题。
+23 -4
View File
@@ -4,6 +4,8 @@
本项目的测试平台`https://2662r3426b.vicp.fun`,从开放以来就使用了该方法,效果良好。
教程可参考B站博主`毕乐labs`发布的视频教程:[《开源小智服务器xiaozhi-server自动更新以及最新版本MCP接入点配置保姆教程》](https://www.bilibili.com/video/BV15H37zHE7Q)
# 开始条件
- 你的电脑/服务器是linux操作系统
- 你已经跑通了整个流程
@@ -40,6 +42,9 @@ git clone https://ghproxy.net/https://github.com/xinnan-tech/xiaozhi-esp32-serve
此刻你需要把`model.pt`文件复制到新的目录去,你可以这样
```
# 创建需要的目录
mkdir -p /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/
cp 你原来的.config.yaml完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/.config.yaml
cp 你原来的model.pt完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/model.pt
```
@@ -100,6 +105,8 @@ else
fi
nohup java -jar xiaozhi-esp32-api.jar --spring.profiles.active=dev &
tail tail -f nohup.out
```
保存好后执行赋权命令
@@ -129,8 +136,12 @@ else
echo "已杀掉进程 $PID"
fi
cd main/xiaozhi-server
# 初始化conda环境
source ~/.bashrc
conda activate xiaozhi-esp32-server
pip install -r requirements.txt
nohup python app.py >/dev/null &
tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
```
保存好后执行赋权命令
@@ -144,8 +155,6 @@ chmod 777 update_8000.sh
以上的脚本都建立好后,日常更新,我们只要依次执行以下命令就可以做到自动更新和启动
```
# 进入pyhton环境
conda activate xiaozhi-esp32-server
cd /home/system/xiaozhi
# 更新并启动Java程序
./update_8001.sh
@@ -153,11 +162,21 @@ cd /home/system/xiaozhi
./update_8002.sh
# 更新并启动python程序
./update_8000.sh
# 查看Java日志
# 后期想查看java日志,执行以下命令
tail -f nohup.out
# 查看Python日志
# 后期想查看python日志,执行以下命令
tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
```
# 注意事项
测试平台`https://2662r3426b.vicp.fun`,是使用nginx做了反向代理。nginx.conf详细配置可以[参考这里](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)
## 常见问题
### 1、为什么没有见到8001端口?
回答:8001是开发环境使用的,用于运行前端的端口。如果你是服务器部署,不建议使用`npm run serve`启动8001端口运行前端,而是像本教程一样编译成html文件,然后使用nginx来管理访问。
### 2、每次更新需要更新手动SQL语句吗?
回答:不需要,因为项目使用**Liquibase**管理数据库版本,会自动执行新的sql脚本。
+29
View File
@@ -0,0 +1,29 @@
# 火山双向流式TTS+声音克隆配置教程
单模块部署下,使用火山引擎双向流式语音合成服务的同时进行声音克隆,支持WebSocket协议流式调用。
### 1.开通火山引擎服务
访问 https://console.volcengine.com/speech/app 在应用管理创建应用,勾选语音合成大模型和声音复刻大模型,左边列表点击声音复刻大模型后下滑获得App IdAccess TokenCluster ID以及声音ID(S_xxxxx)
### 2.克隆音色
克隆音色请参照教程 https://github.com/104gogo/huoshan-voice-copy
准备一段 10-30 秒的音频文件(.wav格式)添加到克隆的项目中,将平台获得的密钥填入```uploadAndStatus.py```和```tts_http_demo.py```
在uploadAndStatus.py中,将 audio_path=修改成自己的.wav文件名称
```python
train(appid=appid, token=token, audio_path=r".\audios\xiaohe.wav", spk_id=spk_id)
```
运行以下命令生成test_submit.mp3,点击播放试听克隆效果
```python
python uploadAndStatus.py
python tts_http_demo.py
```
回到火山引擎控制台页面,刷新可以看到声音复刻详情的状态是复刻成功。
### 3.填写配置文件
将火山引擎服务申请到的密钥填入.config.yaml的HuoshanDoubleStreamTTS配置文件中
修改 resource_id的参数为``` volc.megatts.default```
(参考官方文档 https://www.volcengine.com/docs/6561/1329505
speaker的参数填入声音ID(S_xxxxx)
启动服务,唤醒小智发出的声音是克隆的音色即成功。
Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

+165
View File
@@ -0,0 +1,165 @@
# IndexStreamTTS 使用指南
## 环境准备
### 1. 克隆项目 (这里使用的为VLLM的版本)
```bash
git clone https://github.com/Ksuriuri/index-tts-vllm.git
cd index-tts-vllm
```
### 2. 创建并激活 conda 环境
```bash
conda create -n index-tts-vllm python=3.12
conda activate index-tts-vllm
```
### 3. 安装PyTorch
#### 查看显卡最高支持的版本和实际安装的版本
```bash
nvidia-smi
nvcc --version
```
#### 驱动支持的最高 CUDA 版本
```bash
CUDA Version: 12.8
```
#### 实际安装的 CUDA 编译器版本
```bash
Cuda compilation tools, release 12.8, V12.8.89
```
#### 那么对应的安装命令 (请注意不要横跨大版本!!!)
```bash
pip install torch==2.7.0 torchvision==0.22.0 torchaudio==2.7.0 --index-url https://download.pytorch.org/whl/cu128
```
优先建议安装 pytorch 2.7.0(对应 vllm 0.9.0),具体安装指令请参考:[pytorch 官网](https://pytorch.org/get-started/locally/]\)
若显卡不支持,请安装 pytorch 2.5.1(对应 vllm 0.7.3),并将 requirements.txt 中 vllm==0.9.0 修改为 vllm==0.7.3
### 4. 安装依赖
```bash
pip install -r requirements.txt
```
### 5. 下载模型权重
此为官方权重文件,下载到本地任意路径即可,支持 IndexTTS-1.5 的权重
| HuggingFace | ModelScope |
|---------------------------------------------------------------|---------------------------------------------------------------------|
| [IndexTTS](https://huggingface.co/IndexTeam/Index-TTS) | [IndexTTS](https://modelscope.cn/models/IndexTeam/Index-TTS) |
| [IndexTTS-1.5](https://huggingface.co/IndexTeam/IndexTTS-1.5) | [IndexTTS-1.5](https://modelscope.cn/models/IndexTeam/IndexTTS-1.5) |
下面以ModelScope的安装方法为例
### 请注意:git需要安装并初始化启用lfs(如已安装可以跳过)
```bash
sudo apt-get install git-lfs
git lfs install
```
创建模型目录,并拉取模型
```bash
mkkdir model_dir
cd model_dir
git clone https://www.modelscope.cn/IndexTeam/IndexTTS-1.5.git
```
### 5. 模型权重转换
```bash
bash convert_hf_format.sh /path/to/your/model_dir
```
例如:你下载的IndexTTS-1.5模型存放在model_dir目录下,则执行以下命令
```bash
bash convert_hf_format.sh model_dir/IndexTTS-1.5
```
此操作会将官方的模型权重转换为 transformers 库兼容的版本,保存在模型权重路径下的 vllm 文件夹中,方便后续 vllm 库加载模型权重
### 6. 更改接口适配一下项目
接口返回数据与项目不适配需要调整一下,使其直接返回音频数据
```bash
@app.post("/tts", responses={
200: {"content": {"application/octet-stream": {}}},
500: {"content": {"application/json": {}}}
})
async def tts_api(request: Request):
try:
data = await request.json()
text = data["text"]
character = data["character"]
global tts
sr, wav = await tts.infer_with_ref_audio_embed(character, text)
return Response(content=wav.tobytes(), media_type="application/octet-stream")
except Exception as ex:
tb_str = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))
print(tb_str)
return JSONResponse(
status_code=500,
content={
"status": "error",
"error": str(tb_str)
}
)
```
### 7.编写sh启动脚本(请注意要在相应的conda环境下运行)
```bash
vi start_api.sh
```
### 将下面内容粘贴进去并按:输入wq保存
#### 脚本中的/home/system/indexTTS/index-tts-vllm/model_dir/IndexTTS-1.5 请自行修改为实际路径
```bash
# 激活conda环境
conda activate index-tts-vllm
echo "激活项目conda环境"
sleep 2
# 查找占用11996端口的进程号
PID_VLLM=$(sudo netstat -tulnp | grep 11996 | awk '{print $7}' | cut -d'/' -f1)
# 检查是否找到进程号
if [ -z "$PID_VLLM" ]; then
echo "没有找到占用11996端口的进程"
else
echo "找到占用11996端口的进程,进程号为: $PID_VLLM"
# 先尝试普通kill,等待2秒
kill $PID_VLLM
sleep 2
# 检查进程是否还在
if ps -p $PID_VLLM > /dev/null; then
echo "进程仍在运行,强制终止..."
kill -9 $PID_VLLM
fi
echo "已终止进程 $PID_VLLM"
fi
# 创建tmp目录(如果不存在)
mkdir -p tmp
# 后台运行api_server.py,日志重定向到tmp/server.log
export VLLM_USE_V1=0
nohup python api_server.py --model_dir /home/system/indexTTS/index-tts-vllm/model_dir/IndexTTS-1.5 --port 11996 > tmp/server.log 2>&1 &
echo "api_server.py 已在后台运行,日志请查看 tmp/server.log"
```
给脚本执行权限并运行脚本
```bash
chmod +x tmp
./start_api.sh
```
日志会在tmp/server.log中输出,可以通过以下命令查看日志情况
```bash
tail -f tmp/server.log
```
## 音色配置
index-tts-vllm支持通过配置文件注册自定义音色,支持单音色和混合音色配置。
在项目根目录下的assets/speaker.json文件中配置自定义音色
### 配置格式说明
```bash
{
"说话人名称1": [
"音频文件路径1.wav",
"音频文件路径2.wav"
],
"说话人名称2": [
"音频文件路径3.wav"
]
}
```
### 注意
添加后需在智控台中添加相应的说话人(单模块则更换相应的voice)
+125
View File
@@ -0,0 +1,125 @@
# MCP 接入点部署使用指南
本教程包含3个部分
- 1、如何部署MCP接入点这个服务
- 2、全模块部署时,怎么配置MCP接入点
- 3、单模块部署时,怎么配置MCP接入点
# 1、如何部署MCP接入点这个服务
## 第一步,下载mcp接入点项目源码
浏览器打开[mcp接入点项目地址](https://github.com/xinnan-tech/mcp-endpoint-server)
打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。
点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`mcp-endpoint-server-main`
你需要把它重命名成`mcp-endpoint-server`
## 第二步,启动程序
这个项目是一个很简单的项目,建议使用docker运行。不过如果你不想使用docker运行,你可以参考[这个页面](https://github.com/xinnan-tech/mcp-endpoint-server/blob/main/README_dev.md)使用源码运行。以下是docker运行的方法
```
# 进入本项目源码根目录
cd mcp-endpoint-server
# 清除缓存
docker compose -f docker-compose.yml down
docker stop mcp-endpoint-server
docker rm mcp-endpoint-server
docker rmi ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest
# 启动docker容器
docker compose -f docker-compose.yml up -d
# 查看日志
docker logs -f mcp-endpoint-server
```
此时,日志里会输出类似以下的日志
```
250705 INFO-=====下面的地址分别是智控台/单模块MCP接入点地址====
250705 INFO-智控台MCP参数配置: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
250705 INFO-单模块部署MCP接入点: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
250705 INFO-=====请根据具体部署选择使用,请勿泄露给任何人======
```
请你把两个接口地址复制出来:
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么
原来我的接口地址
```
智控台MCP参数配置: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
单模块部署MCP接入点: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
```
就要改成
```
智控台MCP参数配置: http://192.168.1.25:8004/mcp_endpoint/health?key=abc
单模块部署MCP接入点: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
```
改好后,请使用浏览器直接访问`智控台MCP参数配置`。当浏览器出现类似这样的代码,说明是成功了。
```
{"result":{"status":"success","connections":{"tool_connections":0,"robot_connections":0,"total_connections":0}},"error":null,"id":null,"jsonrpc":"2.0"}
```
请你保留好上面两个`接口地址`,下一步要用到。
# 2、全模块部署时,怎么配置MCP接入点
如果你是全模块部署,使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。
然后搜索参数`server.mcp_endpoint`,此时,它的值应该是`null`值。
点击修改按钮,把上一步得来的`智控台MCP参数配置`粘贴到`参数值`里。然后保存。
如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问mcp接入点,很大概率是网络防火墙,或者没有填写正确的局域网ip。
# 3、单模块部署时,怎么配置MCP接入点
如果你是单模块部署,找到你的配置文件`data/.config.yaml`
在配置文件搜索`mcp_endpoint`,如果没有找到,你就增加`mcp_endpoint`配置。类似我是就是这样
```
server:
websocket: ws://你的ip或者域名:端口号/xiaozhi/v1/
http_port: 8002
log:
log_level: INFO
# 此处可能还更多配置..
mcp_endpoint: 你的接入点websocket地址
```
这时,请你把`如何部署MCP接入点这个服务`中得到的`单模块部署MCP接入点` 粘贴到 `mcp_endpoint`中。类似这样
```
server:
websocket: ws://你的ip或者域名:端口号/xiaozhi/v1/
http_port: 8002
log:
log_level: INFO
# 此处可能还更多配置
mcp_endpoint: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
```
配置好后,启动单模块会输出如下的日志。
```
250705[__main__]-INFO-初始化组件: vad成功 SileroVAD
250705[__main__]-INFO-初始化组件: asr成功 FunASRServer
250705[__main__]-INFO-OTA接口是 http://192.168.1.25:8002/xiaozhi/ota/
250705[__main__]-INFO-视觉分析接口是 http://192.168.1.25:8002/mcp/vision/explain
250705[__main__]-INFO-mcp接入点是 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
250705[__main__]-INFO-Websocket地址是 ws://192.168.1.25:8000/xiaozhi/v1/
250705[__main__]-INFO-=======上面的地址是websocket协议地址,请勿用浏览器访问=======
250705[__main__]-INFO-如想测试websocket请用谷歌浏览器打开test目录下的test_page.html
250705[__main__]-INFO-=============================================================
```
如上,如果能输出类似的`mcp接入点是``ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`说明配置成功了。
+42 -82
View File
@@ -1,92 +1,36 @@
# MCP 接入点部署使用指南
# MCP 接入点使用指南
本教程包含2个部分
- 1、如何开启mcp接入点
- 2、如何为智能体接入一个简单的mcp功能,如计算器功能
本教程以虾哥开源的mcp计算器功能为示例,介绍如何将自己自定义的mcp服务接入到自己的接入点里。
部署的前提条件:
- 1、你已经部署了全模块,因为mcp接入点需要全模块中的智控台功能
- 2、你想在不修改xiaozhi-server项目的前提下,扩展小智的功能
# 如何开启mcp接入点
## 第一步,下载mcp接入点项目源码
浏览器打开[mcp接入点项目地址](https://github.com/xinnan-tech/mcp-endpoint-server)
打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。
点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`mcp-endpoint-server-main`
你需要把它重命名成`mcp-endpoint-server`
## 第二步,启动程序
这个项目是一个很简单的项目,建议使用docker运行。不过如果你不想使用docker运行,你可以参考[这个页面](https://github.com/xinnan-tech/mcp-endpoint-server/blob/main/README_dev.md)使用源码运行。以下是docker运行的方法
```
# 进入本项目源码根目录
cd mcp-endpoint-server
# 清除缓存
docker compose -f docker-compose.yml down
docker stop mcp-endpoint-server
docker rm mcp-endpoint-server
docker rmi ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest
# 启动docker容器
docker compose -f docker-compose.yml up -d
# 查看日志
docker logs -f mcp-endpoint-server
```
此时,日志里会输出类似以下的日志
```
======================================================
接口地址: http://172.1.1.1:8004/mcp_endpoint/health?key=xxxx
=======上面的地址是MCP接入点地址,请勿泄露给任何人============
```
请你把接口地址复制出来:
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么
原来我的接口地址
```
http://172.1.1.1:8004/mcp_endpoint/health?key=xxxx
```
就要改成
```
http://192.168.1.25:8004/mcp_endpoint/health?key=xxxx
```
改好后,请使用浏览器直接访问这个接口。当浏览器出现类似这样的代码,说明是成功了。
```
{"result":{"status":"success","connections":{"tool_connections":0,"robot_connections":0,"total_connections":0}},"error":null,"id":null,"jsonrpc":"2.0"}
```
请你保留好这个`接口地址`,下一步要用到。
## 第三步,配置智控台
使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。
然后搜索参数`server.mcp_endpoint`,此时,它的值应该是`null`值。
点击修改按钮,把上一步得来的`接口地址`粘贴到`参数值`里。然后保存。
如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问mcp接入点,很大概率是网络防火墙,或者没有填写正确的局域网ip。
本教程的前提是,你的`xiaozhi-server`已经启用了mcp接入点功能,如果你还没启用,可以先根据[这个教程](./mcp-endpoint-enable.md)启用。
# 如何为智能体接入一个简单的mcp功能,如计算器功能
如果以上步骤顺利,你可以进入智能体管理,点击`配置角色`,在`意图识别`的右边,有一个`编辑功能`的按钮。
### 如果你是全模块部署
如果你是全模块部署,你可以进入智控台,智能体管理,点击`配置角色`,在`意图识别`的右边,有一个`编辑功能`的按钮。
点击这个按钮。在弹出的页面里,位于底部,会有`MCP接入点`,正常来说,会显示这个智能体的`MCP接入点地址`,接下来,我们来给这个智能体扩展一个基于MCP技术的计算器的功能。
这个`MCP接入点地址`很重要,你等一下会用到。
### 如果你是单模块部署
如果你是单模块部署,且你已经在配置文件里配置了MCP接入点地址,那么正常来说,单模块部署启动的时候,会输出如下的日志。
```
250705[__main__]-INFO-初始化组件: vad成功 SileroVAD
250705[__main__]-INFO-初始化组件: asr成功 FunASRServer
250705[__main__]-INFO-OTA接口是 http://192.168.1.25:8002/xiaozhi/ota/
250705[__main__]-INFO-视觉分析接口是 http://192.168.1.25:8002/mcp/vision/explain
250705[__main__]-INFO-mcp接入点是 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
250705[__main__]-INFO-Websocket地址是 ws://192.168.1.25:8000/xiaozhi/v1/
250705[__main__]-INFO-=======上面的地址是websocket协议地址,请勿用浏览器访问=======
250705[__main__]-INFO-如想测试websocket请用谷歌浏览器打开test目录下的test_page.html
250705[__main__]-INFO-=============================================================
```
如上,输出`mcp接入点是``ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`就是你的`MCP接入点地址`
这个`MCP接入点地址`很重要,你等一下会用到。
## 第一步 下载虾哥MCP计算器项目代码
浏览器打开虾哥写的[计算器项目](https://github.com/78/mcp-calculator)
@@ -114,13 +58,13 @@ pip install -r requirements.txt
例如我的智能体的mcp地址是
```
ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
```
开始输入命令
```bash
export MCP_ENDPOINT=ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
export MCP_ENDPOINT=ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
```
输入完后,启动程序
@@ -129,6 +73,22 @@ export MCP_ENDPOINT=ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
python mcp_pipe.py calculator.py
```
### 如果你是智控台部署
如果你是智控台部署,启动完后,你再进入智控台,点击刷新MCP的接入状态,就会看到你扩展的功能列表了。
启动完后,你再进入智控台,点击刷新MCP的接入状态,就会看到你扩展的功能列表了。
### 如果你是单模块部署
如果你是单模块部署,当设备连接后,会输出类似的日志,说明成功了
```
250705 -INFO-正在初始化MCP接入点: wss://2662r3426b.vicp.fun/mcp_e
250705 -INFO-发送MCP接入点初始化消息
250705 -INFO-MCP接入点连接成功
250705 -INFO-MCP接入点初始化成功
250705 -INFO-统一工具处理器初始化完成
250705 -INFO-MCP接入点服务器信息: name=Calculator, version=1.9.4
250705 -INFO-MCP接入点支持的工具数量: 1
250705 -INFO-所有MCP接入点工具已获取,客户端准备就绪
250705 -INFO-工具缓存已刷新
250705 -INFO-当前支持的函数列表: [ 'get_time', 'get_lunar', 'play_music', 'get_weather', 'handle_exit_intent', 'calculator']
```
如果包含了 `'calculator'`,说明设备将可以根据意图识别,调用计算器这个工具。
+127
View File
@@ -0,0 +1,127 @@
# MQTT 网关部署教程
`xiaozhi-esp32-server`项目,可结合虾哥开源的[xiaozhi-mqtt-gateway](https://github.com/78/xiaozhi-mqtt-gateway) 项目进行简单改造,即可实现小智硬件MQTT+UDP连接。
## 准备工作
查看你智控台首页底部的版本号,确认你的智控台版本是否是`0.7.7`及以上版本。如果不是,需要升级智控台。
准备好你的`xiaozhi-server``mqtt-websocket`连接地址。在你原来的`websocket地址`基础上,添加`?from=mqtt_gateway`字符,就可以得到`mqtt-websocket`连接地址
1、如果你是源码部署,你的`mqtt-websocket`地址是:
```
ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway
```
2、如果你是docker部署,你的`mqtt-websocket`地址是
```
ws://你宿主机局域网IP:8000/xiaozhi/v1?from=mqtt_gateway
```
## 第一步 部署MQTT网关
1. 克隆[改造后的xiaozhi-mqtt-gateway项目](https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git)
```bash
git clone https://ghfast.top/https://github.com/xinnan-tech/xiaozhi-mqtt-gateway.git
cd xiaozhi-mqtt-gateway
```
2. 安装依赖:
```bash
npm install
npm install -g pm2
```
3. 配置 `config.json`
```bash
cp config/mqtt.json.example config/mqtt.json
```
4. 编辑配置文件 config/mqtt.json,把你在`本文准备阶段``mqtt-websocket`地址替换到`chat_servers`里。例如源码部署的`xiaozhi-server`就是如下配置:
```
{
"production": {
"chat_servers": [
"ws://127.0.0.1:8000/xiaozhi/v1?from=mqtt_gateway"
]
},
"debug": false,
"max_mqtt_payload_size": 8192,
"mcp_client": {
"capabilities": {
},
"client_info": {
"name": "xiaozhi-mqtt-client",
"version": "1.0.0"
},
"max_tools_count": 128
}
}
```
5. 在项目根目录创建下`.env`文件,并设置以下环境变量:
```
PUBLIC_IP=your-ip # 服务器公网IP
MQTT_PORT=1883 # MQTT服务器端口
UDP_PORT=8884 # UDP服务器端口
MQTT_SIGNATURE_KEY=test # MQTT签名密钥
```
请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。
`MQTT_SIGNATURE_KEY` 是用于MQTT连接认证的密钥,最好设置成复杂一点的,这个密钥稍后还会用到。
6. 启动MQTT网关
```
# 启动服务
pm2 start ecosystem.config.js
# 查看日志
pm2 logs xz-mqtt
```
当你看到如下日志,说明MQTT网关启动成功:
```
0|xz-mqtt | 2025-09-11T12:14:48: MQTT 服务器正在监听端口 1883
0|xz-mqtt | 2025-09-11T12:14:48: UDP 服务器正在监听 x.x.x.x:8884
```
如果需要重启MQTT网关,执行如下命令:
```
pm2 restart xz-mqtt
```
## 第二步 配置智控台
1. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_gateway`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`MQTT_PORT`。类似这样
```
192.168.0.7:1883
```
2. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_signature_key`,点击编辑,填入你在`.env`文件中设置的`MQTT_SIGNATURE_KEY`。
3. 在智控台顶部,点击`参数管理`,搜索`server.udp_gateway`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`UDP_PORT`。类似这样
```
192.168.0.7:8884
```
上面的配置完成后,你可以使用curl命令,验证你的ota地址是否会下发mqtt配置,把下面的`http://localhost:8002/xiaozhi/ota/`改成你的ota地址
```
curl 'http://localhost:8002/xiaozhi/ota/' \
-H 'Content-Type: application/json' \
-H 'Client-Id: 7b94d69a-9808-4c59-9c9b-704333b38aff' \
-H 'Device-Id: 11:22:33:44:55:66' \
--data-raw $'{\n "application": {\n "version": "1.0.1",\n "elf_sha256": "1"\n },\n "board": {\n "mac": "11:22:33:44:55:66"\n }\n}'
```
如果返回的内容包含`mqtt`相关的配置,说明配置成功。类似这样
```
{"server_time":{"timestamp":1757567894012,"timeZone":"Asia/Shanghai","timezone_offset":480},"activation":{"code":"460609","message":"http://xiaozhi.server.com\n460609","challenge":"11:22:33:44:55:66"},"firmware":{"version":"1.0.1","url":"http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL"},"websocket":{"url":"ws://192.168.4.23:8000/xiaozhi/v1/"},"mqtt":{"endpoint":"192.168.0.7:1883","client_id":"GID_default@@@11_22_33_44_55_66@@@7b94d69a-9808-4c59-9c9b-704333b38aff","username":"eyJpcCI6IjA6MDowOjA6MDowOjA6MSJ9","password":"Y8XP9xcUhVIN9OmbCHT9ETBiYNE3l3Z07Wk46wV9PE8=","publish_topic":"device-server","subscribe_topic":"devices/p2p/11_22_33_44_55_66"}}
```
## 第三步 重启小智设备
由于MQTT信息是需要靠OTA地址下发的,因此只有你保证能正常连接服务器的OTA地址,重启唤醒即可。
唤醒后留意mqtt-gateway的日志,确认是否有连接成功的日志。
```
pm2 logs xz-mqtt
```
+99
View File
@@ -0,0 +1,99 @@
# PaddleSpeechTTS集成xiaozhi服务
## 一、基础环境要求
操作系统:Windows / Linux / WSL 2
Python 版本:3.9以上(请根据Paddle官方教程调整)
Paddle 版本:官方最新版本 ```https://www.paddlepaddle.org.cn/install```
依赖管理工具:conda 或 venv
## 二、启动paddlespeech服务
### 1.从paddlespeech官方仓库拉取源码
```bash
git clone https://github.com/PaddlePaddle/PaddleSpeech.git
```
### 2.建立虚拟环境
```bash
#请根据Paddle官方支持的python版本建立环境 ```https://www.paddlepaddle.org.cn/install```
conda create -n paddle_env python=3.10 -y
conda activate paddle_env
```
### 3.进入paddlespeech目录
```bash
cd PaddleSpeech
```
### 4.安装paddlespeech
```bash
pip install pytest-runner -i https://pypi.tuna.tsinghua.edu.cn/simple
#以下命令使用任意一个
pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
pip install paddlespeech -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### 5.使用命令自动下载语音模型
```bash
paddlespeech tts --input "你好,这是一次测试"
```
此步骤会自动下载模型缓存至本地 .paddlespeech/models 目录
### 6.修改tts_online_application.yaml配置
参考目录 ```"PaddleSpeech\demos\streaming_tts_server\conf\tts_online_application.yaml"```
选择```tts_online_application.yaml```文件用编辑器打开,设置```protocol```为```websocket```
### 7.启动服务
```yaml
paddlespeech_server start --config_file ./demos/streaming_tts_server/conf/tts_online_application.yaml
#官方默认启动命令:
paddlespeech_server start --config_file ./conf/tts_online_application.yaml
```
请根据你的```tts_online_application.yaml```的实际目录来启动命令,看到如下日志即启动成功
```
Prefix dict has been built successfully.
[2025-08-07 10:03:11,312] [ DEBUG] __init__.py:166 - Prefix dict has been built successfully.
INFO: Started server process [2298]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8092 (Press CTRL+C to quit)
```
## 三、修改小智的配置文件
### 1.```main/xiaozhi-server/core/providers/tts/paddle_speech.py```
### 2.```main/xiaozhi-server/data/.config.yaml```
使用单模块部署
```yaml
selected_module:
TTS: PaddleSpeechTTS
TTS:
PaddleSpeechTTS:
type: paddle_speech
protocol: websocket
url: ws://127.0.0.1:8092/paddlespeech/tts/streaming # TTS 服务的 URL 地址,指向本地服务器 [websocket默认ws://127.0.0.1:8092/paddlespeech/tts/streaming]
spk_id: 0 # 发音人 ID,0 通常表示默认的发音人
sample_rate: 24000 # 采样率 [websocket默认24000http默认0 自动选择]
speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢
volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小
save_path: # 保存路径
```
### 3.启动xiaozhi服务
```py
python app.py
```
打开test目录下的test_page.html,测试连接和发送消息时paddlespeech端是否有输出日志
输出日志参考:
```
INFO: 127.0.0.1:44312 - "WebSocket /paddlespeech/tts/streaming" [accepted]
INFO: connection open
[2025-08-07 11:16:33,355] [ INFO] - sentence: 哈哈,怎么突然找我聊天啦?
[2025-08-07 11:16:33,356] [ INFO] - The durations of audio is: 2.4625 s
[2025-08-07 11:16:33,356] [ INFO] - first response time: 0.1143045425415039 s
[2025-08-07 11:16:33,356] [ INFO] - final response time: 0.4777836799621582 s
[2025-08-07 11:16:33,356] [ INFO] - RTF: 0.19402382942625715
[2025-08-07 11:16:33,356] [ INFO] - Other info: front time: 0.06514096260070801 s, first am infer time: 0.008037090301513672 s, first voc infer time: 0.04112648963928223 s,
[2025-08-07 11:16:33,356] [ INFO] - Complete the synthesis of the audio streams
INFO: connection closed
```
+27
View File
@@ -0,0 +1,27 @@
# 语音识别、大语言模型、非流式语音合成、流式语音合成、视觉模型的性能测试工具使用指南
1.在main/xiaozhi-server目录下创建data目录
2.在data目录下创建.config.yaml文件
3.在.data/config.yaml中,写入你的语音识别、大语言模型、流式语音合成、视觉模型的参数
例如:
```
LLM:
ChatGLMLLM:
# 定义LLM API类型
type: openai
# glm-4-flash 是免费的,但是还是需要注册填写api_key的
# 可在这里找到你的api key https://bigmodel.cn/usercenter/proj-mgmt/apikeys
model_name: glm-4-flash
url: https://open.bigmodel.cn/api/paas/v4/
api_key: 你的chat-glm web key
TTS:
VLLM:
ASR:
```
4.在main/xiaozhi-server目录下运行performance_tester.py:
```
python performance_tester.py
```
+233
View File
@@ -0,0 +1,233 @@
# 声纹识别启用指南
本教程包含3个部分
- 1、如何部署声纹识别这个服务
- 2、全模块部署时,怎么配置声纹识别接口
- 3、最简化部署时,怎么配置声纹识别
# 1、如何部署声纹识别这个服务
## 第一步,下载声纹识别项目源码
浏览器打开[声纹识别项目地址](https://github.com/xinnan-tech/voiceprint-api)
打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。
点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`voiceprint-api-main`
你需要把它重命名成`voiceprint-api`
## 第二步, 创建数据库和表
声纹识别需要依赖`mysql`数据库。如果你之前已经部署`智控台`,说明你已经安装了`mysql`。你可以共用它。
你可以你试一下在宿主机使用`telnet`命令,看看能不能正常访问`mysql``3306`端口。
```
telnet 127.0.0.1 3306
```
如果能访问到3306端口,请忽略以下的内容,直接进入第三步。
如果不能访问,你需要回忆一下,你的`mysql`是怎么安装的。
如果你的mysql是通过自己使用安装包安装的,说明你的`mysql`做了网络隔离。你可能先解访问`mysql``3306`端口这个问题。
如果你`mysql`是通过本项目的`docker-compose_all.yml`安装的。你需要找一下你当时创建数据库的`docker-compose_all.yml`文件,修改以下的内容
修改前
```
xiaozhi-esp32-server-db:
...
networks:
- default
expose:
- "3306:3306"
```
修改后
```
xiaozhi-esp32-server-db:
...
networks:
- default
ports:
- "3306:3306"
```
注意是将`xiaozhi-esp32-server-db`下面的`expose`改成`ports`。改完后,需要重新启动。以下是重启mysql的命令:
```
# 进入你docker-compose_all.yml所在的文件夹,例如我的是xiaozhi-server
cd xiaozhi-server
docker compose -f docker-compose_all.yml down
docker compose -f docker-compose.yml up -d
```
启动完后,在宿主机再使用`telnet`命令,看看能不能正常访问`mysql``3306`端口。
```
telnet 127.0.0.1 3306
```
正常来说这样就可以访问的了。
## 第三步, 创建数据库和表
如果你的宿主机,能正常访问mysql数据库,那就在mysql上创建一个名字为`voiceprint_db`的数据库和`voiceprints`表。
```
CREATE DATABASE voiceprint_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE voiceprint_db;
CREATE TABLE voiceprints (
id INT AUTO_INCREMENT PRIMARY KEY,
speaker_id VARCHAR(255) NOT NULL UNIQUE,
feature_vector LONGBLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_speaker_id (speaker_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
## 第四步, 配置数据库连接
进入`voiceprint-api`文件夹,创建名字为`data`的文件夹。
`voiceprint-api`根目录里的`voiceprint.yaml`,复制到`data`的文件夹,将它重命名为`.voiceprint.yaml`
接下来,你需要重点配置一下`.voiceprint.yaml`里的数据库连接。
```
mysql:
host: "127.0.0.1"
port: 3306
user: "root"
password: "your_password"
database: "voiceprint_db"
```
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
## 第五步,启动程序
这个项目是一个很简单的项目,建议使用docker运行。不过如果你不想使用docker运行,你可以参考[这个页面](https://github.com/xinnan-tech/voiceprint-api/blob/main/README.md)使用源码运行。以下是docker运行的方法
```
# 进入本项目源码根目录
cd voiceprint-api
# 清除缓存
docker compose -f docker-compose.yml down
docker stop voiceprint-api
docker rm voiceprint-api
docker rmi ghcr.nju.edu.cn/xinnan-tech/voiceprint-api:latest
# 启动docker容器
docker compose -f docker-compose.yml up -d
# 查看日志
docker logs -f voiceprint-api
```
此时,日志里会输出类似以下的日志
```
250711 INFO-🚀 开始: 生产环境服务启动(Uvicorn),监听地址: 0.0.0.0:8005
250711 INFO-============================================================
250711 INFO-声纹接口地址: http://127.0.0.1:8005/voiceprint/health?key=abcd
250711 INFO-============================================================
```
请你把声纹接口地址复制出来:
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么
原来我的接口地址
```
http://127.0.0.1:8005/voiceprint/health?key=abcd
```
就要改成
```
http://192.168.1.25:8005/voiceprint/health?key=abcd
```
改好后,请使用浏览器直接访问`声纹接口地址`。当浏览器出现类似这样的代码,说明是成功了。
```
{"total_voiceprints":0,"status":"healthy"}
```
请你保留好修改后的`声纹接口地址`,下一步要用到。
# 2、全模块部署时,怎么配置声纹识别
## 第一步 配置接口
如果你是全模块部署,使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。
然后搜索参数`server.voice_print`,此时,它的值应该是`null`值。
点击修改按钮,把上一步得来的`声纹接口地址`粘贴到`参数值`里。然后保存。
如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问声纹识别,很大概率是网络防火墙,或者没有填写正确的局域网ip。
## 第二步 设置智能体记忆模式
进入你的智能体的角色配置里,将记忆设置成`本地短期记忆`,一定要开启`上报文字+语音`
## 第三步 和你的智能体聊天
将你的设备通电,然后和他用正常的语速和音调聊天。
## 第四步 设置声纹
在智控台,`智能体管理`页面,在智能体的面板里,有一个`声纹识别`按钮,点击它。在底部有一个`新增按钮`。就可以对某个人说的话进行声纹注册。
在弹出的框里,`描述`这个属性建议填写上,可以是这个人的职业、性格、爱好。方便智能体对说话人进行分析和了解。
## 第三步 和你的智能体聊天
将你的设备通电,问它,你知道我是谁吗?如果他能回答得出,说明声纹识别功能正常。
# 3、最简化部署时,怎么配置声纹识别
## 第一步 配置接口
打开 `xiaozhi-server/data/.config.yaml` 文件(如果没有需要创建),然后添加/修改以下内容:
```
# 声纹识别配置
voiceprint:
# 声纹接口地址
url: 你的声纹接口地址
# 说话人配置:speaker_id,名称,描述
speakers:
- "test1,张三,张三是一个程序员"
- "test2,李四,李四是一个产品经理"
- "test3,王五,王五是一个设计师"
```
把上一步得来的 `声纹接口地址` 粘贴到 `url` 里。然后保存。
`speakers` 参数依据需求添加。这里需要注意这个 `speaker_id` 参数,后面注册声纹会用到。
## 第二步 注册声纹
如果你已经启动了声纹服务,本地浏览器里访问 `http://localhost:8005/voiceprint/docs` 即可查看 API 文档,这里只说明注册声纹的 API 如何使用。
注册声纹的 API 地址为 `http://localhost:8005/voiceprint/register`,请求方式为 POST。
请求头需要包含 Bearer Token 认证,token 为 `声纹接口地址``?key=` 后的部分,比如如果我的声纹注册地址为 `http://127.0.0.1:8005/voiceprint/health?key=abcd`,那么我的 token 就是`abcd`
请求体包含说话人 IDspeaker_id),和 WAV 音频文件(file),请求示例如下:
```
curl -X POST \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "speaker_id=your_speaker_id_here" \
-F "file=@/path/to/your/file" \
http://localhost:8005/voiceprint/register
```
这里的 `file` 是要注册的说话人说话的音频文件, `speaker_id` 需要和第一步配置接口的 `speaker_id` 保持一致。比如说我需要注册张三的声纹,在 `.config.yaml` 中填的张三的 `speaker_id``test1`,那么我注册张三声纹的时候,请求体里填的 `speaker_id` 就是 `test1` `file` 填的就是张三说一段话的音频文件。
## 第三步 启动服务
启动小智服务器和声纹服务,即可正常使用。
+64
View File
@@ -0,0 +1,64 @@
# 天气插件使用指南
## 概述
天气插件 `get_weather` 是小智ESP32语音助手的核心功能之一,支持通过语音查询全国各地的天气信息。插件基于和风天气API,提供实时天气和7天天气预报功能。
## API Key 申请指南
### 1. 注册和风天气账号
1. 访问 [和风天气控制台](https://console.qweather.com/)
2. 注册账号并完成邮箱验证
3. 登录控制台
### 2. 创建应用获取API Key
1. 进入控制台后,点击右侧["项目管理"](https://console.qweather.com/project?lang=zh) → "创建项目"
2. 填写项目信息:
- **项目名称**:如"小智语音助手"
3. 点击保存
4. 项目创建完成后,在该项目中点击"创建凭据"
5. 填写凭据信息:
- **凭据名称**:如"小智语音助手"
- **身份认证方式**:选择"API Key"
6. 点击保存
7. 在凭据中复制`API Key`,这是第一个关键的配置信息
### 3. 获取API Host
1. 在控制台中点击["设置"](https://console.qweather.com/setting?lang=zh) → "API Host"
2. 查看分配给你的专属`API Host`地址,这个是第二个关键的配置信息
以上操作,会得到两个重要的配置信息:`API Key``API Host`
## 配置方式(任选一种)
### 方式1. 如果你使用了智控台部署(推荐)
1. 登录智控台
2. 进入"角色配置"页面
3. 选择要配置的智能体
4. 点击"编辑功能"按钮
5. 在右侧参数配置区域找到"天气查询"插件
6. 勾选"天气查询"
7. 将复制过来的第一个关键配置`API Key`,填入到`天气插件 API 密钥`
8. 将复制过来的第二个关键配置`API Host`,填入到`开发者 API Host`
9. 保存配置,再保存智能体配置
### 方式2. 如果你只是单模块xiaozhi-server部署
`data/.config.yaml` 中配置:
1. 将复制过来的第一个关键配置`API Key`,填入到`api_key`
2. 将复制过来的第二个关键配置`API Host`,填入到`api_host`
3. 将你所在的城市填入到`default_location`里,例如`广州`
```yaml
plugins:
get_weather:
api_key: "你的和风天气API密钥"
api_host: "你的和风天气API主机地址"
default_location: "你的默认查询城市"
```
+72
View File
@@ -8,6 +8,7 @@
* [3.1. `xiaozhi-server` (核心AI引擎 - Python实现)](#31-xiaozhi-server-核心ai引擎---python实现)
* [3.2. `manager-api` (管理后端 - Java Spring Boot实现)](#32-manager-api-管理后端---java-spring-boot实现)
* [3.3. `manager-web` (Web管理前端 - Vue.js实现)](#33-manager-web-web管理前端---vuejs实现)
* [3.4. `manager-mobile` (移动管理端 - uni-app+Vue3实现)](#34-manager-mobile-移动管理端---uni-appvue3实现)
4. [数据流与交互机制](#4-数据流与交互机制)
5. [核心功能概要](#5-核心功能概要)
6. [部署与配置概述](#6-部署与配置概述)
@@ -58,6 +59,14 @@
* (潜在功能)监控系统运行状态、查看日志、进行故障排查等。
*`manager-api` 提供的所有后端管理功能进行全面的交互。
5. **`manager-mobile` (智控台移动版 - uni-app实现):**
这是一个基于uni-app v3 + Vue 3 + Vite的跨端移动管理端,支持AppAndroid & iOS)和微信小程序。其主要能力包括:
* 提供移动设备上的便捷管理界面,与manager-web功能类似但针对移动端进行了优化。
* 支持用户登录、设备管理、AI服务配置等核心功能。
* 跨平台适配,一套代码可同时运行在iOS、Android和微信小程序上。
* 基于alova + @alova/adapter-uniapp实现网络请求,与manager-api无缝集成。
* 使用pinia进行状态管理,确保数据一致性。
**高层交互流程概述:**
* **语音交互主线:** **ESP32设备**捕捉到用户语音后,通过**WebSocket**将音频数据实时传输给**`xiaozhi-server`**。`xiaozhi-server`完成一系列AI处理(VAD、ASR、LLM交互、TTS)后,再通过WebSocket将合成的语音回复发送回ESP32设备进行播放。所有与语音直接相关的实时交互均在此链路完成。
@@ -71,6 +80,7 @@ xiaozhi-esp32-server
├─ xiaozhi-server 8000 端口 Python语言开发 负责与esp32通信
├─ manager-web 8001 端口 Node.js+Vue开发 负责提供控制台的web界面
├─ manager-api 8002 端口 Java语言开发 负责提供控制台的api
└─ manager-mobile 跨平台移动应用 uni-app+Vue3开发 负责提供移动版智控台管理
```
---
@@ -294,6 +304,68 @@ xiaozhi-esp32-server
---
### 3.4. `manager-mobile` (智控台移动版 - uni-app实现)
`manager-mobile` 组件是一个基于uni-app v3 + Vue 3 + Vite的跨端移动管理端,支持AppAndroid & iOS)和微信小程序。它为系统管理员提供了移动端的管理界面,使得管理操作更加便捷。
* **核心目标:**
* 提供移动设备上的便捷管理界面,与manager-web功能类似但针对移动端进行了优化。
* 支持用户登录、设备管理、AI服务配置等核心功能。
* 跨平台适配,一套代码可同时运行在iOS、Android和微信小程序上。
* 为移动用户提供流畅、高效的管理体验。
* **平台兼容性:**
| H5 | iOS | Android | 微信小程序 |
| -- | --- | ------- | ---------- |
| √ | √ | √ | √ |
* **核心技术栈:**
* **uni-app v3:** 一个使用Vue.js开发所有前端应用的框架,支持iOS、Android、H5、以及各种小程序。
* **Vue 3:** 用于构建用户界面的渐进式框架,提供了更好的性能和新特性。
* **Vite:** 下一代前端开发与构建工具,提供极速的开发体验。
* **pnpm:** 快速、节省磁盘空间的包管理器。
* **alova:** 轻量级、灵活的请求策略库,搭配@alova/adapter-uniapp适配uni-app环境。
* **pinia:** Vue的状态管理库,替代Vuex,提供更简洁的API和更好的TypeScript支持。
* **UnoCSS:** 具有高性能且极具灵活性的即时原子化CSS引擎。
* **TypeScript:** 提供类型安全的开发体验。
* **关键实现细节:**
1. **跨平台架构:**
* 基于uni-app框架,实现了一套代码多端运行的目标,大幅减少了开发和维护成本。
* 针对不同平台的特性和限制,通过条件编译进行平台特定的代码处理。
2. **项目结构:**
* **`src/App.vue`:** 应用的根组件,定义了全局的样式和配置。
* **`src/main.ts`:** 应用的入口文件,负责初始化Vue实例、注册插件和路由拦截器。
* **`src/pages/`:** 存放应用的页面组件,如登录页、设备管理页等。
* **`src/layouts/`:** 定义应用的布局组件,如默认布局、带tabbar的布局等。
* **`src/api/`:** 封装与后端API的通信逻辑。
* **`src/store/`:** 使用pinia进行状态管理。
* **`src/components/`:** 存放可复用的组件。
* **`src/utils/`:** 提供通用的工具函数。
3. **网络请求:**
* 基于alova + @alova/adapter-uniapp实现网络请求,统一处理请求头、认证、错误等。
* 请求地址和环境配置通过.env文件管理,支持不同环境的切换。
4. **路由与鉴权:**
* 使用uni-app的路由系统,结合路由拦截器实现页面的登录验证和权限控制。
* 未登录用户访问需要认证的页面时,会被重定向到登录页。
5. **状态管理:**
* 使用pinia管理应用状态,如用户信息、设备列表等。
* 通过pinia-plugin-persistedstate插件实现状态的持久化存储。
6. **构建与发布:**
* 支持多种构建命令,如构建微信小程序、Android和iOS App等。
* 使用HBuilderX进行App的云打包,简化了打包流程。
`manager-mobile` 通过这些技术的应用,为用户提供了一个功能完备、体验流畅的移动端管理工具,使得管理员可以随时随地进行系统管理和配置。
---
## 4. 数据流与交互机制
`xiaozhi-esp32-server` 系统通过各组件间定义清晰的数据流和交互协议来协同工作。主要的通信方式依赖于针对实时交互优化的WebSocket协议和适用于客户端-服务器请求的RESTful API。
+66 -1
View File
@@ -8,6 +8,7 @@
* [3.1. `xiaozhi-server` (Core AI Engine - Python Implementation)](#31-xiaozhi-server-core-ai-engine---python-implementation)
* [3.2. `manager-api` (Management Backend - Java Spring Boot Implementation)](#32-manager-api-management-backend---java-spring-boot-implementation)
* [3.3. `manager-web` (Web Management Frontend - Vue.js Implementation)](#33-manager-web-web-management-frontend---vuejs-implementation)
* [3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)](#34-manager-mobile-mobile-management-console---uniapp-implementation)
4. [Data Flow and Interaction Mechanisms](#4-data-flow-and-interaction-mechanisms)
5. [Key Features Summary](#5-key-features-summary)
6. [Deployment and Configuration Overview](#6-deployment-and-configuration-overview)
@@ -72,6 +73,7 @@ xiaozhi-esp32-server
├─ xiaozhi-server Port 8000 Python development Responsible for ESP32 communication
├─ manager-web Port 8001 Node.js+Vue development Responsible for providing web interface for console
├─ manager-api Port 8002 Java development Responsible for providing console API
└─ manager-mobile Cross-platform mobile application uni-app+Vue3 development Responsible for providing mobile console management
```
---
@@ -190,7 +192,70 @@ The `manager-web` is a Single Page Application (SPA) providing the administrativ
4. **State Management (`src/store/index.js`):** Vuex manages global state (user info, device lists, etc.) via state, getters, mutations, and actions (often involving API calls).
5. **API Communication (`src/apis/`):** Modularized API service files make asynchronous calls to `manager-api`.
6. **Build Process & PWA Features:** Vue CLI (Webpack) bundles assets. Workbox enables PWA features like caching.
7. **Environment Configuration (`.env` files):** Manages settings like the `manager-api` base URL for different environments.
7. **Environment Configuration (`.env` files):**
* The `.env` (and `.env.development`, `.env.production`, etc.) files in the project root directory are used to define environment variables. These variables (such as `VUE_APP_API_BASE_URL` to specify the base URL of `manager-api`) can be accessed in the application code through `process.env.VUE_APP_XXX`, allowing configuration of different parameters for different build environments (development, testing, production).
`manager-web` constructs a powerful, maintainable, and user-friendly management interface through the comprehensive application of these technologies, providing solid frontend support for the configuration and monitoring of the `xiaozhi-esp32-server` system.
### 3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)
The `manager-mobile` component is a cross-platform mobile management application based on uni-app v3 + Vue 3 + Vite, supporting App (Android & iOS) and WeChat Mini Program. It provides system administrators with a mobile management interface, making management operations more convenient.
* **Core Objectives:**
* Provide a convenient management interface on mobile devices, similar in functionality to manager-web but optimized for mobile platforms.
* Support core functions such as user login, device management, and AI service configuration.
* Cross-platform adaptation, allowing a single codebase to run on iOS, Android, and WeChat Mini Programs.
* Provide mobile users with a smooth and efficient management experience.
* **Platform Compatibility:**
| H5 | iOS | Android | WeChat Mini Program |
| -- | --- | ------- | ------------------ |
| × | √ | √ | √ |
* **Core Technologies:**
* **uni-app v3:** A framework for developing all frontend applications using Vue.js, supporting iOS, Android, H5, and various mini-programs.
* **Vue 3:** A progressive framework for building user interfaces, providing better performance and new features.
* **Vite:** The next generation frontend development and build tool, offering an extremely fast development experience.
* **pnpm:** A fast, disk space-efficient package manager.
* **alova:** A lightweight, flexible request strategy library, paired with @alova/adapter-uniapp to adapt to the uni-app environment.
* **pinia:** State management library for Vue, replacing Vuex, providing a simpler API and better TypeScript support.
* **UnoCSS:** A high-performance and extremely flexible instant atomic CSS engine.
* **TypeScript:** Provides a type-safe development experience.
* **Key Implementation Details:**
1. **Cross-Platform Architecture:**
* Based on the uni-app framework, achieving the goal of "write once, run anywhere," significantly reducing development and maintenance costs.
* Handling platform-specific code through conditional compilation to address the characteristics and limitations of different platforms.
2. **Project Structure:**
* **`src/App.vue`:** The root component of the application, defining global styles and configurations.
* **`src/main.ts`:** The entry file of the application, responsible for initializing the Vue instance, registering plugins, and setting up route interceptors.
* **`src/pages/`:** Stores application page components, such as login pages, device management pages, etc.
* **`src/layouts/`:** Defines application layout components, such as default layouts, layouts with tabbar, etc.
* **`src/api/`:** Encapsulates communication logic with backend APIs.
* **`src/store/`:** Uses pinia for state management.
* **`src/components/`:** Stores reusable components.
* **`src/utils/`:** Provides common utility functions.
3. **Network Requests:**
* Implements network requests based on alova + @alova/adapter-uniapp, unified handling of request headers, authentication, errors, etc.
* Request addresses and environment configurations are managed through .env files, supporting switching between different environments.
4. **Routing and Authentication:**
* Uses uni-app's routing system, combined with route interceptors to implement page login verification and permission control.
* When unlogged users access pages requiring authentication, they are redirected to the login page.
5. **State Management:**
* Uses pinia to manage application state, such as user information, device lists, etc.
* Implements persistent storage of state through the pinia-plugin-persistedstate plugin.
6. **Build and Release:**
* Supports multiple build commands, such as building WeChat Mini Programs, Android, and iOS Apps.
* Uses HBuilderX for cloud packaging of Apps, simplifying the packaging process.
`manager-mobile` provides users with a fully functional, smooth mobile management tool through the application of these technologies, allowing administrators to perform system management and configuration anytime, anywhere.
---
+16
View File
@@ -24,6 +24,8 @@
<hutool.version>5.8.24</hutool.version>
<jsoup.version>1.19.1</jsoup.version>
<knife4j.version>4.6.0</knife4j.version>
<springdoc.version>2.8.8</springdoc.version>
<commons-lang3.version>3.18.0</commons-lang3.version>
<shiro.version>2.0.2</shiro.version>
<captcha.version>1.6.2</captcha.version>
<guava.version>33.0.0-jre</guava.version>
@@ -75,6 +77,7 @@
<version>${captcha.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
@@ -190,11 +193,24 @@
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<!--knife4j -->
<dependency>
<groupId>com.github.xingfudeshi</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 日常工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -91,6 +91,11 @@ public interface Constant {
*/
String SERVER_WEBSOCKET = "server.websocket";
/**
* mqtt gateway 配置
*/
String SERVER_MQTT_GATEWAY = "server.mqtt_gateway";
/**
* ota地址
*/
@@ -116,6 +121,11 @@ public interface Constant {
*/
String SERVER_MCP_ENDPOINT = "server.mcp_endpoint";
/**
* mcp接入点路径
*/
String SERVER_VOICE_PRINT = "server.voice_print";
/**
* 无记忆
*/
@@ -232,7 +242,7 @@ public interface Constant {
/**
* 版本号
*/
public static final String VERSION = "0.6.1";
public static final String VERSION = "0.8.1";
/**
* 无效固件URL
@@ -0,0 +1,21 @@
package xiaozhi.common.utils;
import lombok.Data;
/**
* JSON-RPC2.0 格式规范对象
*/
@Data
public class JsonRpcTwo {
private String jsonrpc = "2.0";
private String method;
private Object params;
private Integer id;
public JsonRpcTwo(String method, Object params, Integer id) {
this.method = method;
this.params = params;
this.id = id;
}
}
@@ -0,0 +1,21 @@
package xiaozhi.modules.agent.Enums;
import lombok.Getter;
/**
* 智能体聊天记录类型
*/
@Getter
public enum AgentChatHistoryType {
USER((byte) 1),
AGENT((byte) 2);
private final byte value;
AgentChatHistoryType(byte i) {
this.value = i;
}
}
@@ -0,0 +1,44 @@
package xiaozhi.modules.agent.Enums;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.JsonRpcTwo;
import java.util.Map;
/**
* 小智MCP JSON-RPC 请求json
*/
public class XiaoZhiMcpJsonRpcJson {
//小智初始化mcp请求json
private static final String INITIALIZE_JSON;
//小智mcp初始化成功,返回通知请求json
private static final String NOTIFICATIONS_INITIALIZED_JSON;
//小智mcp获取mcp工具集合请求json
private static final String TOOLS_LIST_REQUEST;
// 延迟加载
static {
INITIALIZE_JSON = JsonUtils.toJsonString(new JsonRpcTwo("initialize",
Map.of(
"protocolVersion", "2024-11-05",
"capabilities", Map.of(
"roots", Map.of("listChanged", false),
"sampling", Map.of()),
"clientInfo", Map.of(
"name", "xz-mcp-broker",
"version", "0.0.1")),
1));
NOTIFICATIONS_INITIALIZED_JSON = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
TOOLS_LIST_REQUEST = JsonUtils.toJsonString(new JsonRpcTwo("tools/list", null, 2));
}
public static String getInitializeJson(){
return INITIALIZE_JSON;
}
public static String getNotificationsInitializedJson(){
return NOTIFICATIONS_INITIALIZED_JSON;
}
public static String getToolsListJson(){
return TOOLS_LIST_REQUEST;
}
}
@@ -47,6 +47,7 @@ import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -181,6 +182,33 @@ public class AgentController {
List<AgentChatHistoryDTO> result = agentChatHistoryService.getChatHistoryBySessionId(id, sessionId);
return new Result<List<AgentChatHistoryDTO>>().ok(result);
}
@GetMapping("/{id}/chat-history/user")
@Operation(summary = "获取智能体聊天记录(用户)")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentChatHistoryUserVO>> getRecentlyFiftyByAgentId(
@PathVariable("id") String id) {
// 获取当前用户
UserDetail user = SecurityUser.getUser();
// 检查权限
if (!agentService.checkAgentPermission(id, user.getId())) {
return new Result<List<AgentChatHistoryUserVO>>().error("没有权限查看该智能体的聊天记录");
}
// 查询聊天记录
List<AgentChatHistoryUserVO> data = agentChatHistoryService.getRecentlyFiftyByAgentId(id);
return new Result<List<AgentChatHistoryUserVO>>().ok(data);
}
@GetMapping("/{id}/chat-history/audio")
@Operation(summary = "获取音频内容")
@RequiresPermissions("sys:role:normal")
public Result<String> getContentByAudioId(
@PathVariable("id") String id) {
// 查询聊天记录
String data = agentChatHistoryService.getContentByAudioId(id);
return new Result<String>().ok(data);
}
@PostMapping("/audio/{audioId}")
@Operation(summary = "获取音频下载ID")
@@ -0,0 +1,86 @@
package xiaozhi.modules.agent.controller;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.service.AgentVoicePrintService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.security.user.SecurityUser;
import xiaozhi.modules.sys.service.SysParamsService;
@Tag(name = "智能体声纹管理")
@AllArgsConstructor
@RestController
@RequestMapping("/agent/voice-print")
public class AgentVoicePrintController {
private final AgentVoicePrintService agentVoicePrintService;
private final SysParamsService sysParamsService;
@PostMapping
@Operation(summary = "创建智能体的声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> save(@RequestBody @Valid AgentVoicePrintSaveDTO dto) {
boolean b = agentVoicePrintService.insert(dto);
if (b) {
return new Result<>();
}
return new Result<Void>().error("智能体的声纹创建失败");
}
@PutMapping
@Operation(summary = "更新智能体的对应声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> update(@RequestBody @Valid AgentVoicePrintUpdateDTO dto) {
Long userId = SecurityUser.getUserId();
boolean b = agentVoicePrintService.update(userId, dto);
if (b) {
return new Result<>();
}
return new Result<Void>().error("智能体的对应声纹更新失败");
}
@DeleteMapping("/{id}")
@Operation(summary = "删除智能体对应声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> delete(@PathVariable String id) {
Long userId = SecurityUser.getUserId();
// 先删除关联的设备
boolean delete = agentVoicePrintService.delete(userId, id);
if (delete) {
return new Result<>();
}
return new Result<Void>().error("智能体的对应声纹删除失败");
}
@GetMapping("/list/{id}")
@Operation(summary = "获取用户指定智能体声纹列表")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentVoicePrintVO>> list(@PathVariable String id) {
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
throw new RenException("声纹接口未配置,请先在参数配置中配置声纹接口地址(server.voice_print)");
}
Long userId = SecurityUser.getUserId();
List<AgentVoicePrintVO> list = agentVoicePrintService.list(userId, id);
return new Result<List<AgentVoicePrintVO>>().ok(list);
}
}
@@ -0,0 +1,20 @@
package xiaozhi.modules.agent.dao;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
/**
* {@link AgentChatHistoryEntity} 智能体聊天历史记录Dao对象
*
* @author Goody
* @version 1.0, 2025/4/30
* @since 1.0.0
*/
@Mapper
public interface AgentVoicePrintDao extends BaseMapper<AgentVoicePrintEntity> {
}
@@ -0,0 +1,28 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* 保存智能体声纹的dto
*
* @author zjy
*/
@Data
public class AgentVoicePrintSaveDTO {
/**
* 关联的智能体id
*/
private String agentId;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
}
@@ -0,0 +1,28 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* 修改智能体声纹的dto
*
* @author zjy
*/
@Data
public class AgentVoicePrintUpdateDTO {
/**
* 智能体声纹id
*/
private String id;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
}
@@ -0,0 +1,21 @@
package xiaozhi.modules.agent.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 声纹识别接口返回的对象
*/
@Data
public class IdentifyVoicePrintResponse {
/**
* 最匹配的声纹id
*/
@JsonProperty("speaker_id")
private String speakerId;
/**
* 声纹的分数
*/
private Double score;
}
@@ -1,32 +0,0 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* MCP JSON-RPC 请求 DTO
*/
@Data
public class McpJsonRpcRequest {
private String jsonrpc = "2.0";
private String method;
private Object params;
private Integer id;
public McpJsonRpcRequest() {
}
public McpJsonRpcRequest(String method) {
this.method = method;
}
public McpJsonRpcRequest(String method, Object params, Integer id) {
this.method = method;
this.params = params;
this.id = id;
}
public McpJsonRpcRequest(String method, Object params) {
this.method = method;
this.params = params;
}
}
@@ -0,0 +1,64 @@
package xiaozhi.modules.agent.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 智能体声纹表
*
* @author zjy
*/
@TableName(value = "ai_agent_voice_print")
@Data
public class AgentVoicePrintEntity {
/**
* 主键id
*/
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/**
* 关联的智能体id
*/
private String agentId;
/**
* 关联的音频id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long creator;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createDate;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}
@@ -9,6 +9,7 @@ import xiaozhi.common.page.PageData;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
/**
* 智能体聊天记录表处理service
@@ -44,4 +45,30 @@ public interface AgentChatHistoryService extends IService<AgentChatHistoryEntity
* @param deleteText 是否删除文本
*/
void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText);
/**
* 根据智能体ID获取最近50条用户的聊天记录数据(带音频数据)
*
* @param agentId 智能体id
* @return 聊天记录列表(只有用户)
*/
List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId);
/**
* 根据音频数据ID获取聊天内容
*
* @param audioId 音频id
* @return 聊天内容
*/
String getContentByAudioId(String audioId);
/**
* 查询此音频id是否属于此智能体
*
* @param audioId 音频id
* @param agentId 音频id
* @return T:属于 F:不属于
*/
boolean isAudioOwnedByAgent(String audioId,String agentId);
}
@@ -0,0 +1,50 @@
package xiaozhi.modules.agent.service;
import java.util.List;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
/**
* 智能体声纹处理service
*
* @author zjy
*/
public interface AgentVoicePrintService {
/**
* 添加智能体新的声纹
*
* @param dto 保存智能体声纹的数据
* @return T:成功 F:失败
*/
boolean insert(AgentVoicePrintSaveDTO dto);
/**
* 删除智能体的指的声纹
*
* @param userId 当前登录的用户id
* @param voicePrintId 声纹id
* @return 是否成功 T:成功 F:失败
*/
boolean delete(Long userId, String voicePrintId);
/**
* 获取指定智能体的所有声纹数据
*
* @param userId 当前登录的用户id
* @param agentId 智能体id
* @return 声纹数据集合
*/
List<AgentVoicePrintVO> list(Long userId, String agentId);
/**
* 更新智能体的指的声纹数据
*
* @param userId 当前登录的用户id
* @param dto 修改的声纹的数据
* @return 是否成功 T:成功 F:失败
*/
boolean update(Long userId, AgentVoicePrintUpdateDTO dto);
}
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -16,11 +17,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.page.PageData;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
/**
* 智能体聊天记录表处理service {@link AgentChatHistoryService} impl
@@ -90,4 +94,78 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
}
}
@Override
public List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId) {
// 构建查询条件(不添加按照创建时间排序,数据本来就是主键越大创建时间越大
// 不添加这样可以减少排序全部数据在分页的全盘扫描消耗)
LambdaQueryWrapper<AgentChatHistoryEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.select(AgentChatHistoryEntity::getContent, AgentChatHistoryEntity::getAudioId)
.eq(AgentChatHistoryEntity::getAgentId, agentId)
.eq(AgentChatHistoryEntity::getChatType, AgentChatHistoryType.USER.getValue())
.isNotNull(AgentChatHistoryEntity::getAudioId)
// 添加此行,确保查询结果按照创建时间降序排列
// 使用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
// id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
.orderByDesc(AgentChatHistoryEntity::getId);
// 构建分页查询,查询前50页数据
Page<AgentChatHistoryEntity> pageParam = new Page<>(0, 50);
IPage<AgentChatHistoryEntity> result = this.baseMapper.selectPage(pageParam, wrapper);
return result.getRecords().stream().map(item -> {
AgentChatHistoryUserVO vo = ConvertUtils.sourceToTarget(item, AgentChatHistoryUserVO.class);
// 处理 content 字段,确保只返回聊天内容
if (vo != null && vo.getContent() != null) {
vo.setContent(extractContentFromString(vo.getContent()));
}
return vo;
}).toList();
}
/**
* 从 content 字段中提取聊天内容
* 如果 content 是 JSON 格式(如 {"speaker": "未知说话人", "content": "现在几点了。"}),则提取 content
* 字段
* 如果 content 是普通字符串,则直接返回
*
* @param content 原始内容
* @return 提取的聊天内容
*/
private String extractContentFromString(String content) {
if (content == null || content.trim().isEmpty()) {
return content;
}
// 尝试解析为 JSON
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(content, Map.class);
if (jsonMap != null && jsonMap.containsKey("content")) {
Object contentObj = jsonMap.get("content");
return contentObj != null ? contentObj.toString() : content;
}
} catch (Exception e) {
// 如果不是有效的 JSON,直接返回原内容
}
// 如果不是 JSON 格式或没有 content 字段,直接返回原内容
return content;
}
@Override
public String getContentByAudioId(String audioId) {
AgentChatHistoryEntity agentChatHistoryEntity = baseMapper
.selectOne(new LambdaQueryWrapper<AgentChatHistoryEntity>()
.select(AgentChatHistoryEntity::getContent)
.eq(AgentChatHistoryEntity::getAudioId, audioId));
return agentChatHistoryEntity == null ? null : agentChatHistoryEntity.getContent();
}
@Override
public boolean isAudioOwnedByAgent(String audioId, String agentId) {
// 查询是否有指定音频id和智能体id的数据,如果有且只有一条说明此数据属性此智能体
Long row = baseMapper.selectCount(new LambdaQueryWrapper<AgentChatHistoryEntity>()
.eq(AgentChatHistoryEntity::getAudioId, audioId)
.eq(AgentChatHistoryEntity::getAgentId, agentId));
return row == 1;
}
}
@@ -18,7 +18,7 @@ import xiaozhi.common.constant.Constant;
import xiaozhi.common.utils.AESUtils;
import xiaozhi.common.utils.HashEncryptionUtil;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dto.McpJsonRpcRequest;
import xiaozhi.modules.agent.Enums.XiaoZhiMcpJsonRpcJson;
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
import xiaozhi.modules.sys.service.SysParamsService;
import xiaozhi.modules.sys.utils.WebSocketClientManager;
@@ -61,57 +61,79 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
wsUrl = wsUrl.replace("/mcp/", "/call/");
try {
// 创建 WebSocket 连接
// 创建 WebSocket 连接,增加超时时间到15秒
try (WebSocketClientManager client = WebSocketClientManager.build(
new WebSocketClientManager.Builder()
.uri(wsUrl)
.connectTimeout(5, TimeUnit.SECONDS)
.maxSessionDuration(9, TimeUnit.SECONDS))) {
.bufferSize(1024 * 1024)
.connectTimeout(8, TimeUnit.SECONDS)
.maxSessionDuration(10, TimeUnit.SECONDS))) {
// 发送初始化消息
McpJsonRpcRequest initializeRequest = new McpJsonRpcRequest("initialize",
Map.of(
"protocolVersion", "2024-11-05",
"capabilities", Map.of(
"roots", Map.of("listChanged", false),
"sampling", Map.of()),
"clientInfo", Map.of(
"name", "xz-mcp-broker",
"version", "0.0.1")),
1);
client.sendJson(initializeRequest);
// 步骤1: 发送初始化消息并等待响应
log.info("发送MCP初始化消息,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getInitializeJson());
// 等待初始化响应
Thread.sleep(200);
// 发送初始化完成通知
// 对于通知类型的消息,手动构建JSON以避免包含null字段
String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
client.sendText(notificationJson);
// 等待 0.2 秒
Thread.sleep(200);
// 发送工具列表请求
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
client.sendJson(toolsRequest);
// 监听响应,直到收到包含 id=2 的响应(tools/list响应)
List<String> responses = client.listener(response -> {
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
List<String> initResponses = client.listenerWithoutClose(response -> {
try {
// 先尝试解析为通用JSON对象来获取id
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
// 检查是否有result字段,表示初始化成功
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
}
return false;
} catch (Exception e) {
log.warn("解析响应失败: {}", response, e);
log.warn("解析初始化响应失败: {}", response, e);
return false;
}
});
// 处理响应
for (String response : responses) {
// 验证初始化响应
boolean initSucceeded = false;
for (String response : initResponses) {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
if (jsonMap.containsKey("result")) {
log.info("MCP初始化成功,智能体ID: {}", id);
initSucceeded = true;
break;
} else if (jsonMap.containsKey("error")) {
log.error("MCP初始化失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
log.warn("处理初始化响应失败: {}", response, e);
}
}
if (!initSucceeded) {
log.error("未收到有效的MCP初始化响应,智能体ID: {}", id);
return List.of();
}
// 步骤2: 发送初始化完成通知 - 只有在收到initialize响应后才发送
log.info("发送MCP初始化完成通知,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getNotificationsInitializedJson());
// 步骤3: 发送工具列表请求 - 立即发送,无需额外延迟
log.info("发送MCP工具列表请求,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getToolsListJson());
// 等待工具列表响应 (id=2)
List<String> toolsResponses = client.listener(response -> {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
} catch (Exception e) {
log.warn("解析工具列表响应失败: {}", response, e);
return false;
}
});
// 处理工具列表响应
for (String response : toolsResponses) {
try {
// 先解析为通用JSON对象
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
// 检查是否有result字段
@@ -122,11 +144,16 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
if (toolsObj instanceof List) {
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
// 提取工具名称列表
return toolsList.stream()
List<String> result = toolsList.stream()
.map(tool -> (String) tool.get("name"))
.filter(name -> name != null)
.collect(Collectors.toList());
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
return result;
}
} else if (jsonMap.containsKey("error")) {
log.error("获取工具列表失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
@@ -134,12 +161,12 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
}
}
log.warn("未找到有效的工具列表响应");
log.warn("未找到有效的工具列表响应,智能体ID: {}", id);
return List.of();
}
} catch (Exception e) {
log.error("获取智能体 MCP 工具列表失败,智能体ID: {}", id, e);
log.error("获取智能体 MCP 工具列表失败,智能体ID: {},错误原因:{}", id, e.getMessage());
return List.of();
}
}
@@ -204,4 +231,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 加密后成token值
return AESUtils.encrypt(key, json);
}
}
}
@@ -41,6 +41,7 @@ import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.model.dto.ModelProviderDTO;
import xiaozhi.modules.model.entity.ModelConfigEntity;
import xiaozhi.modules.model.service.ModelConfigService;
import xiaozhi.modules.model.service.ModelProviderService;
import xiaozhi.modules.security.user.SecurityUser;
@@ -324,9 +325,35 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
// 删除音频数据
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false);
}
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
if (!b) {
throw new RenException("LLM大模型和Intent意图识别,选择参数不匹配");
}
this.updateById(existingEntity);
}
/**
* 验证大语言模型和意图识别的参数是否符合匹配
*
* @param llmModelId 大语言模型id
* @param intentModelId 意图识别id
* @return T 匹配 : F 不匹配
*/
private boolean validateLLMIntentParams(String llmModelId, String intentModelId) {
if (StringUtils.isBlank(llmModelId)) {
return true;
}
ModelConfigEntity llmModelData = modelConfigService.selectById(llmModelId);
String type = llmModelData.getConfigJson().get("type").toString();
// 如果查询大语言模型是openai或者ollama,意图识别选参数都可以
if ("openai".equals(type) || "ollama".equals(type)) {
return true;
}
// 除了openai和ollama的类型,不可以选择id为Intent_function_call(函数调用)的意图识别
return !"Intent_function_call".equals(intentModelId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createAgent(AgentCreateDTO dto) {
@@ -0,0 +1,410 @@
package xiaozhi.modules.agent.service.impl;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.dto.IdentifyVoicePrintResponse;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
import xiaozhi.modules.agent.service.AgentChatAudioService;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentVoicePrintService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.sys.service.SysParamsService;
/**
* @author zjy
*/
@Service
@Slf4j
public class AgentVoicePrintServiceImpl extends ServiceImpl<AgentVoicePrintDao, AgentVoicePrintEntity>
implements AgentVoicePrintService {
private final AgentChatAudioService agentChatAudioService;
private final RestTemplate restTemplate;
private final SysParamsService sysParamsService;
private final AgentChatHistoryService agentChatHistoryService;
// Springboot提供的编程事务类
private final TransactionTemplate transactionTemplate;
// 识别度
private final Double RECOGNITION = 0.5;
private final Executor taskExecutor;
public AgentVoicePrintServiceImpl(AgentChatAudioService agentChatAudioService, RestTemplate restTemplate,
SysParamsService sysParamsService, AgentChatHistoryService agentChatHistoryService,
TransactionTemplate transactionTemplate, @Qualifier("taskExecutor") Executor taskExecutor) {
this.agentChatAudioService = agentChatAudioService;
this.restTemplate = restTemplate;
this.sysParamsService = sysParamsService;
this.agentChatHistoryService = agentChatHistoryService;
this.transactionTemplate = transactionTemplate;
this.taskExecutor = taskExecutor;
}
@Override
public boolean insert(AgentVoicePrintSaveDTO dto) {
// 获取音频数据
ByteArrayResource resource = getVoicePrintAudioWAV(dto.getAgentId(), dto.getAudioId());
// 识别一下此声音是否注册过
IdentifyVoicePrintResponse response = identifyVoicePrint(dto.getAgentId(), resource);
if (response != null && response.getScore() > RECOGNITION) {
// 根据识别出的声纹ID查询对应的用户信息
AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "未知用户";
throw new RenException("此声音声纹对应的人(" + existingUserName + ")已经注册,请选择其他声音注册");
}
AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
// 开启事务
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
// 保存声纹信息
int row = baseMapper.insert(entity);
// 插入一条数据,影响的数据不等于1说明出现了,保存问题回滚
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
// 发送注册声纹请求
registerVoicePrint(entity.getId(), resource);
return true;
} catch (RenException e) {
status.setRollbackOnly(); // 标记事务回滚
throw e;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("保存声纹错误原因:{}", e.getMessage());
throw new RenException("保存声纹错误,请联系管理员");
}
}));
}
@Override
public boolean delete(Long userId, String voicePrintId) {
// 开启事务
boolean b = Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
// 删除声纹,按照指定当前登录用户和智能体
int row = baseMapper.delete(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getId, voicePrintId)
.eq(AgentVoicePrintEntity::getCreator, userId));
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
return true;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("删除声纹存在错误原因:{}", e.getMessage());
throw new RenException("删除声纹出现了错误");
}
}));
// 数据库声纹数据删除成功才继续执行删除声纹服务的数据
if(b){
taskExecutor.execute(()-> {
try {
cancelVoicePrint(voicePrintId);
}catch (RuntimeException e) {
log.error("删除声纹存在运行时错误原因:{},id:{}", e.getMessage(),voicePrintId);
}
});
}
return b;
}
@Override
public List<AgentVoicePrintVO> list(Long userId, String agentId) {
// 按照指定当前登录用户和智能体查找数据
List<AgentVoicePrintEntity> list = baseMapper.selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getAgentId, agentId)
.eq(AgentVoicePrintEntity::getCreator, userId));
return list.stream().map(entity -> {
// 遍历转换成AgentVoicePrintVO类型
return ConvertUtils.sourceToTarget(entity, AgentVoicePrintVO.class);
}).toList();
}
@Override
public boolean update(Long userId, AgentVoicePrintUpdateDTO dto) {
AgentVoicePrintEntity agentVoicePrintEntity = baseMapper
.selectOne(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getId, dto.getId())
.eq(AgentVoicePrintEntity::getCreator, userId));
if (agentVoicePrintEntity == null) {
return false;
}
// 获取音频Id
String audioId = dto.getAudioId();
// 获取智能体id
String agentId = agentVoicePrintEntity.getAgentId();
ByteArrayResource resource;
// audioId不等于空,且audioId和之前的保存的音频id不一样,则需要重新获取音频数据生成声纹
if (!StringUtils.isEmpty(audioId) && !audioId.equals(agentVoicePrintEntity.getAudioId())) {
resource = getVoicePrintAudioWAV(agentId, audioId);
// 识别一下此声音是否注册过
IdentifyVoicePrintResponse response = identifyVoicePrint(agentId, resource);
// 返回分数高于RECOGNITION说明这个声纹已经有了
if (response != null && response.getScore() > RECOGNITION) {
// 判断返回的id如果不是要修改的声纹id,说明这个声纹id,现在要注册的声音已经存在且不是原来的声纹,不允许修改
if (!response.getSpeakerId().equals(dto.getId())) {
// 根据识别出的声纹ID查询对应的用户信息
AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "未知用户";
throw new RenException("此次修改不允许,此声音已经注册为声纹了(" + existingUserName + "");
}
}
} else {
resource = null;
}
// 开启事务
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
int row = baseMapper.updateById(entity);
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
if (resource != null) {
String id = entity.getId();
// 先注销之前这个声纹id上的声纹向量
cancelVoicePrint(id);
// 发送注册声纹请求
registerVoicePrint(id, resource);
}
return true;
} catch (RenException e) {
status.setRollbackOnly(); // 标记事务回滚
throw e;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("修改声纹错误原因:{}", e.getMessage());
throw new RenException("修改声纹错误,请联系管理员");
}
}));
}
/**
* 获取生纹接口URI对象
*
* @return URI对象
*/
private URI getVoicePrintURI() {
// 获取声纹接口地址
String voicePrint = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
try {
return new URI(voicePrint);
} catch (URISyntaxException e) {
log.error("路径格式不正确路径:{}\n错误信息:{}", voicePrint, e.getMessage());
throw new RuntimeException("声纹接口的地址存在错误,请进入参数管理修改声纹接口地址");
}
}
/**
* 获取声纹地址基础路径
*
* @param uri 声纹地址uri
* @return 基础路径
*/
private String getBaseUrl(URI uri) {
String protocol = uri.getScheme();
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
return "%s://%s".formatted(protocol, host);
} else {
return "%s://%s:%s".formatted(protocol, host, port);
}
}
/**
* 获取验证Authorization
*
* @param uri 声纹地址uri
* @return Authorization值
*/
private String getAuthorization(URI uri) {
// 获取参数
String query = uri.getQuery();
// 获取aes加密密钥
String str = "key=";
return "Bearer " + query.substring(query.indexOf(str) + str.length());
}
/**
* 获取声纹音频资源数据
*
* @param audioId 音频Id
* @return 声纹音频资源数据
*/
private ByteArrayResource getVoicePrintAudioWAV(String agentId, String audioId) {
// 判断这个音频是否属于当前智能体
boolean b = agentChatHistoryService.isAudioOwnedByAgent(audioId, agentId);
if (!b) {
throw new RenException("音频数据不属于这个智能体");
}
// 获取到音频数据
byte[] audio = agentChatAudioService.getAudio(audioId);
// 如果音频数据为空的直接报错不进行下去
if (audio == null || audio.length == 0) {
throw new RenException("音频数据是空的请检查上传数据");
}
// 将字节数组包装为资源,返回
return new ByteArrayResource(audio) {
@Override
public String getFilename() {
return "VoicePrint.WAV"; // 设置文件名
}
};
}
/**
* 发送注册声纹http请求
*
* @param id 声纹id
* @param resource 声纹音频资源
*/
private void registerVoicePrint(String id, ByteArrayResource resource) {
// 处理声纹接口地址,获取前缀
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/register";
// 创建请求体
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("speaker_id", id);
body.add("file", resource);
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹注册失败,请求路径:{}", requestUrl);
throw new RenException("声纹保存失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody == null || !responseBody.contains("true")) {
log.error("声纹注册失败,请求处理失败内容:{}", responseBody == null ? "空内容" : responseBody);
throw new RenException("声纹保存失败,请求处理失败");
}
}
/**
* 发送注销声纹的请求
*
* @param voicePrintId 声纹id
*/
private void cancelVoicePrint(String voicePrintId) {
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/" + voicePrintId;
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.DELETE, requestEntity,
String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹注销失败,请求路径:{}", requestUrl);
throw new RenException("声纹注销失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody == null || !responseBody.contains("true")) {
log.error("声纹注销失败,请求处理失败内容:{}", responseBody == null ? "空内容" : responseBody);
throw new RenException("声纹注销失败,请求处理失败");
}
}
/**
* 发送识别声纹http请求
*
* @param agentId 智能体id
* @param resource 声纹音频资源
* @return 返回识别数据
*/
private IdentifyVoicePrintResponse identifyVoicePrint(String agentId, ByteArrayResource resource) {
// 获取该智能体所有注册的声纹
List<AgentVoicePrintEntity> agentVoicePrintList = baseMapper
.selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.select(AgentVoicePrintEntity::getId)
.eq(AgentVoicePrintEntity::getAgentId, agentId));
// 声纹数量为0,说明还没注册过声纹不需要发生识别请求
if (agentVoicePrintList.isEmpty()) {
return null;
}
// 处理声纹接口地址,获取前缀
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/identify";
// 创建请求体
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
// 创建speaker_id参数
String speakerIds = agentVoicePrintList.stream()
.map(AgentVoicePrintEntity::getId)
.collect(Collectors.joining(","));
body.add("speaker_ids", speakerIds);
body.add("file", resource);
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹识别请求失败,请求路径:{}", requestUrl);
throw new RenException("声纹识别失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody != null) {
return JsonUtils.parseObject(responseBody, IdentifyVoicePrintResponse.class);
}
return null;
}
}
@@ -0,0 +1,16 @@
package xiaozhi.modules.agent.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 智能体用户个人聊天数据的VO
*/
@Data
public class AgentChatHistoryUserVO {
@Schema(description = "聊天内容")
private String content;
@Schema(description = "音频ID")
private String audioId;
}
@@ -0,0 +1,33 @@
package xiaozhi.modules.agent.vo;
import lombok.Data;
import java.util.Date;
/**
* 展示智能体声纹列表VO
*/
@Data
public class AgentVoicePrintVO {
/**
* 主键id
*/
private String id;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
/**
* 创建时间
*/
private Date createDate;
}
@@ -9,20 +9,26 @@ import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.agent.entity.AgentPluginMapping;
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.config.service.ConfigService;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -45,6 +51,7 @@ public class ConfigServiceImpl implements ConfigService {
private final TimbreService timbreService;
private final AgentPluginMappingService agentPluginMappingService;
private final AgentMcpAccessPointService agentMcpAccessPointService;
private final AgentVoicePrintDao agentVoicePrintDao;
@Override
public Object getConfig(Boolean isCache) {
@@ -162,6 +169,8 @@ public class ConfigServiceImpl implements ConfigService {
mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/");
result.put("mcp_endpoint", mcpEndpoint);
}
// 获取声纹信息
buildVoiceprintConfig(agent.getId(), result);
// 构建模块配置
buildModuleConfig(
@@ -255,20 +264,90 @@ public class ConfigServiceImpl implements ConfigService {
return config;
}
/**
* 构建声纹配置信息
*
* @param agentId 智能体ID
* @param result 结果Map
*/
private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
try {
// 获取声纹接口地址
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
return;
}
// 获取智能体关联的声纹信息(不需要用户权限验证)
List<AgentVoicePrintVO> voiceprints = getVoiceprintsByAgentId(agentId);
if (voiceprints == null || voiceprints.isEmpty()) {
return;
}
// 构建speakers列表
List<String> speakers = new ArrayList<>();
for (AgentVoicePrintVO voiceprint : voiceprints) {
String speakerStr = String.format("%s,%s,%s",
voiceprint.getId(),
voiceprint.getSourceName(),
voiceprint.getIntroduce() != null ? voiceprint.getIntroduce() : "");
speakers.add(speakerStr);
}
// 构建声纹配置
Map<String, Object> voiceprintConfig = new HashMap<>();
voiceprintConfig.put("url", voiceprintUrl);
voiceprintConfig.put("speakers", speakers);
// 获取声纹识别相似度阈值,默认0.4
String thresholdStr = sysParamsService.getValue("server.voiceprint_similarity_threshold", true);
if (StringUtils.isNotBlank(thresholdStr) && !"null".equals(thresholdStr)) {
try {
double threshold = Double.parseDouble(thresholdStr);
voiceprintConfig.put("similarity_threshold", threshold);
} catch (NumberFormatException e) {
// 如果解析失败,使用默认值0.4
voiceprintConfig.put("similarity_threshold", 0.4);
}
} else {
voiceprintConfig.put("similarity_threshold", 0.4);
}
result.put("voiceprint", voiceprintConfig);
} catch (Exception e) {
// 声纹配置获取失败时不影响其他功能
System.err.println("获取声纹配置失败: " + e.getMessage());
}
}
/**
* 获取智能体关联的声纹信息
*
* @param agentId 智能体ID
* @return 声纹信息列表
*/
private List<AgentVoicePrintVO> getVoiceprintsByAgentId(String agentId) {
LambdaQueryWrapper<AgentVoicePrintEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AgentVoicePrintEntity::getAgentId, agentId);
queryWrapper.orderByAsc(AgentVoicePrintEntity::getCreateDate);
List<AgentVoicePrintEntity> entities = agentVoicePrintDao.selectList(queryWrapper);
return ConvertUtils.sourceToTarget(entities, AgentVoicePrintVO.class);
}
/**
* 构建模块配置
*
* @param prompt 提示词
* @param voice 音色
* @param referenceAudio 参考音频路径
* @param referenceText 参考文本
* @param vadModelId VAD模型ID
* @param asrModelId ASR模型ID
* @param llmModelId LLM模型ID
* @param ttsModelId TTS模型ID
* @param memModelId 记忆模型ID
* @param intentModelId 意图模型ID
* @param result 结果Map
* @param prompt 提示词
* @param voice 音色
* @param referenceAudio 参考音频路径
* @param referenceText 参考文本
* @param vadModelId VAD模型ID
* @param asrModelId ASR模型ID
* @param llmModelId LLM模型ID
* @param ttsModelId TTS模型ID
* @param memModelId 记忆模型ID
* @param intentModelId 意图模型ID
* @param result 结果Map
*/
private void buildModuleConfig(
String assistantName,
@@ -298,14 +377,20 @@ public class ConfigServiceImpl implements ConfigService {
continue;
}
ModelConfigEntity model = modelConfigService.getModelById(modelIds[i], isCache);
if (model == null) {
continue;
}
Map<String, Object> typeConfig = new HashMap<>();
if (model.getConfigJson() != null) {
typeConfig.put(model.getId(), model.getConfigJson());
// 如果是TTS类型,添加private_voice属性
if ("TTS".equals(modelTypes[i])){
if (voice != null) ((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
if (referenceAudio != null) ((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
if (referenceText != null) ((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
if ("TTS".equals(modelTypes[i])) {
if (voice != null)
((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
if (referenceAudio != null)
((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
if (referenceText != null)
((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
}
// 如果是Intent类型,且type=intent_llm,则给他添加附加模型
if ("Intent".equals(modelTypes[i])) {
@@ -51,13 +51,12 @@ public class OTAController {
if (StringUtils.isBlank(clientId)) {
clientId = deviceId;
}
String macAddress = deviceReportReqDTO.getMacAddress();
boolean macAddressValid = isMacAddressValid(macAddress);
boolean macAddressValid = isMacAddressValid(deviceId);
// 设备Id和Mac地址应是一致的, 并且必须需要application字段
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
if (!macAddressValid) {
return createResponse(DeviceReportRespDTO.createError("Invalid device ID"));
}
return createResponse(deviceService.checkDeviceActive(macAddress, clientId, deviceReportReqDTO));
return createResponse(deviceService.checkDeviceActive(deviceId, clientId, deviceReportReqDTO));
}
@Operation(summary = "设备快速检查激活状态")
@@ -78,6 +77,10 @@ public class OTAController {
@GetMapping
@Hidden
public ResponseEntity<String> getOTA() {
String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false);
if (StringUtils.isBlank(mqttUdpConfig)) {
return ResponseEntity.ok("OTA接口不正常,缺少mqtt_gateway地址,请登录智控台,在参数管理找到【server.mqtt_gateway】配置");
}
String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
if (StringUtils.isBlank(wsUrl) || wsUrl.equals("null")) {
return ResponseEntity.ok("OTA接口不正常,缺少websocket地址,请登录智控台,在参数管理找到【server.websocket】配置");
@@ -23,6 +23,9 @@ public class DeviceReportRespDTO {
@Schema(description = "WebSocket配置")
private Websocket websocket;
@Schema(description = "MQTT Gateway配置")
private MQTT mqtt;
@Getter
@Setter
public static class Firmware {
@@ -70,4 +73,21 @@ public class DeviceReportRespDTO {
@Schema(description = "WebSocket服务器地址")
private String url;
}
}
@Getter
@Setter
public static class MQTT {
@Schema(description = "MQTT 配置网址")
private String endpoint;
@Schema(description = "MQTT 客户端唯一标识符")
private String client_id;
@Schema(description = "MQTT 认证用户名")
private String username;
@Schema(description = "MQTT 认证密码")
private String password;
@Schema(description = "ESP32 发布消息的主题")
private String publish_topic;
@Schema(description = "ESP32 订阅的主题")
private String subscribe_topic;
}
}
@@ -1,6 +1,8 @@
package xiaozhi.modules.device.service.impl;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -8,6 +10,9 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
@@ -33,6 +38,7 @@ import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.DateUtils;
import xiaozhi.modules.device.dao.DeviceDao;
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
import xiaozhi.modules.device.dto.DevicePageUserDTO;
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
@@ -44,7 +50,6 @@ import xiaozhi.modules.device.vo.UserShowDeviceListVO;
import xiaozhi.modules.security.user.SecurityUser;
import xiaozhi.modules.sys.service.SysParamsService;
import xiaozhi.modules.sys.service.SysUserUtilService;
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
@Slf4j
@Service
@@ -176,6 +181,23 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
response.setWebsocket(websocket);
// 添加MQTT UDP配置
// 从系统参数获取MQTT Gateway地址,仅在配置有效时使用
String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false);
if (mqttUdpConfig != null && !mqttUdpConfig.equals("null") && !mqttUdpConfig.isEmpty()) {
try {
String groupId = deviceById != null && deviceById.getBoard() != null ? deviceById.getBoard()
: "GID_default";
DeviceReportRespDTO.MQTT mqtt = buildMqttConfig(macAddress, clientId, groupId);
if (mqtt != null) {
mqtt.setEndpoint(mqttUdpConfig);
response.setMqtt(mqtt);
}
} catch (Exception e) {
log.error("生成MQTT配置失败: {}", e.getMessage());
}
}
if (deviceById != null) {
// 如果设备存在,则异步更新上次连接时间和版本信息
String appVersion = deviceReport.getApplication() != null ? deviceReport.getApplication().getVersion()
@@ -437,4 +459,75 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
entity.setAutoUpdate(1);
baseDao.insert(entity);
}
/**
* 生成MQTT密码签名
*
* @param content 签名内容 (clientId + '|' + username)
* @param secretKey 密钥
* @return Base64编码的HMAC-SHA256签名
*/
private String generatePasswordSignature(String content, String secretKey) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmac.init(keySpec);
byte[] signature = hmac.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature);
}
/**
* 构建MQTT配置信息
*
* @param macAddress MAC地址
* @param clientId 客户端ID (UUID)
* @param groupId 分组ID
* @return MQTT配置对象
*/
private DeviceReportRespDTO.MQTT buildMqttConfig(String macAddress, String clientId, String groupId)
throws Exception {
// 从环境变量或系统参数获取签名密钥
String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", false);
if (StringUtils.isBlank(signatureKey)) {
log.warn("缺少MQTT_SIGNATURE_KEY,跳过MQTT配置生成");
return null;
}
// 构建客户端ID格式:groupId@@@macAddress@@@uuid
String groupIdSafeStr = groupId.replace(":", "_");
String deviceIdSafeStr = macAddress.replace(":", "_");
String clientIdSafeStr = clientId.replace(":", "_");
String mqttClientId = String.format("%s@@@%s@@@%s", groupIdSafeStr, deviceIdSafeStr, clientIdSafeStr);
// 构建用户数据(包含IP等信息)
Map<String, String> userData = new HashMap<>();
// 尝试获取客户端IP
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String clientIp = request.getRemoteAddr();
userData.put("ip", clientIp);
}
} catch (Exception e) {
userData.put("ip", "unknown");
}
// 将用户数据编码为Base64 JSON
String userDataJson = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(userData);
String username = Base64.getEncoder().encodeToString(userDataJson.getBytes(StandardCharsets.UTF_8));
// 生成密码签名
String password = generatePasswordSignature(mqttClientId + "|" + username, signatureKey);
// 构建MQTT配置
DeviceReportRespDTO.MQTT mqtt = new DeviceReportRespDTO.MQTT();
mqtt.setClient_id(mqttClientId);
mqtt.setUsername(username);
mqtt.setPassword(password);
mqtt.setPublish_topic("device-server");
mqtt.setSubscribe_topic("devices/p2p/" + deviceIdSafeStr);
return mqtt;
}
}
@@ -21,11 +21,7 @@ import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.config.service.ConfigService;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
import xiaozhi.modules.model.dto.ModelProviderDTO;
import xiaozhi.modules.model.dto.VoiceDTO;
import xiaozhi.modules.model.dto.*;
import xiaozhi.modules.model.entity.ModelConfigEntity;
import xiaozhi.modules.model.service.ModelConfigService;
import xiaozhi.modules.model.service.ModelProviderService;
@@ -52,6 +48,14 @@ public class ModelController {
return new Result<List<ModelBasicInfoDTO>>().ok(modelList);
}
@GetMapping("/llm/names")
@Operation(summary = "获取LLM模型信息")
@RequiresPermissions("sys:role:normal")
public Result<List<LlmModelBasicInfoDTO>> getLlmModelCodeList(@RequestParam(required = false) String modelName) {
List<LlmModelBasicInfoDTO> llmModelCodeList = modelConfigService.getLlmModelCodeList(modelName);
return new Result<List<LlmModelBasicInfoDTO>>().ok(llmModelCodeList);
}
@GetMapping("/{modelType}/provideTypes")
@Operation(summary = "获取模型供应器列表")
@RequiresPermissions("sys:role:superAdmin")
@@ -0,0 +1,13 @@
package xiaozhi.modules.model.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* LLM的模型的基础展示数据
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class LlmModelBasicInfoDTO extends ModelBasicInfoDTO{
private String type;
}
@@ -4,6 +4,7 @@ import java.util.List;
import xiaozhi.common.page.PageData;
import xiaozhi.common.service.BaseService;
import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
@@ -13,6 +14,8 @@ public interface ModelConfigService extends BaseService<ModelConfigEntity> {
List<ModelBasicInfoDTO> getModelCodeList(String modelType, String modelName);
List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName);
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit);
ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -23,6 +24,7 @@ import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.modules.agent.dao.AgentDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.model.dao.ModelConfigDao;
import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
@@ -52,6 +54,25 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
}
@Override
public List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName) {
List<ModelConfigEntity> entities = modelConfigDao.selectList(
new QueryWrapper<ModelConfigEntity>()
.eq("model_type", "llm")
.eq("is_enabled", 1)
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
.select("id", "model_name", "config_json"));
// 处理获取到的内容
return entities.stream().map(item -> {
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
dto.setId(item.getId());
dto.setModelName(item.getModelName());
String type = item.getConfigJson().get("type").toString();
dto.setType(type);
return dto;
}).toList();
}
@Override
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit) {
Map<String, Object> params = new HashMap<String, Object>();
@@ -94,6 +115,21 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
if (CollectionUtil.isEmpty(providerList)) {
throw new RenException("供应器不存在");
}
if (modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
.eq(ModelConfigEntity::getId, llm));
String selectModelType = (modelConfigEntity == null || modelConfigEntity.getModelType() == null) ? null
: modelConfigEntity.getModelType().toUpperCase();
if (modelConfigEntity == null || !"LLM".equals(selectModelType)) {
throw new RenException("设置的LLM不存在");
}
String type = modelConfigEntity.getConfigJson().get("type").toString();
// 如果查询大语言模型是openai或者ollama,意图识别选参数都可以
if (!"openai".equals(type) && !"ollama".equals(type)) {
throw new RenException("设置的LLM不是openai和ollama");
}
}
// 再更新供应器提供的模型
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
@@ -137,6 +173,8 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
.or()
.eq("mem_model_id", modelId)
.or()
.eq("vllm_model_id", modelId)
.or()
.eq("intent_model_id", modelId));
if (!agents.isEmpty()) {
String agentNames = agents.stream()
@@ -107,6 +107,9 @@ public class SysParamsController {
// 验证MCP地址
validateMcpUrl(dto.getParamCode(), dto.getParamValue());
//
validateVoicePrint(dto.getParamCode(), dto.getParamValue());
sysParamsService.update(dto);
configService.getConfig(false);
return new Result<Void>();
@@ -212,13 +215,14 @@ public class SysParamsController {
if (!url.toLowerCase().contains("key")) {
throw new RenException("不是正确的MCP地址");
}
try {
// 发送GET请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
throw new RenException("MCP接口访问失败,状态码:" + response.getStatusCode());
}
// 检查响应内容是否包含OTA相关信息
// 检查响应内容是否包含mcp相关信息
String body = response.getBody();
if (body == null || !body.contains("success")) {
throw new RenException("MCP接口返回内容格式不正确,可能不是一个真实的MCP接口");
@@ -227,4 +231,37 @@ public class SysParamsController {
throw new RenException("MCP接口验证失败:" + e.getMessage());
}
}
// 验证声纹接口地址是否正常
private void validateVoicePrint(String paramCode, String url) {
if (!paramCode.equals(Constant.SERVER_VOICE_PRINT)) {
return;
}
if (StringUtils.isBlank(url) || url.equals("null")) {
throw new RenException("声纹接口地址不能为空");
}
if (url.contains("localhost") || url.contains("127.0.0.1")) {
throw new RenException("声纹接口地址不能使用localhost或127.0.0.1");
}
if (!url.toLowerCase().contains("key")) {
throw new RenException("不是正确的声纹接口地址");
}
// 验证URL格式
if (!url.toLowerCase().startsWith("http")) {
throw new RenException("声纹接口地址必须以http或https开头");
}
try {
// 发送GET请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
throw new RenException("声纹接口访问失败,状态码:" + response.getStatusCode());
}
// 检查响应内容
String body = response.getBody();
if (body == null || !body.contains("healthy")) {
throw new RenException("声纹接口返回内容格式不正确,可能不是一个真实的MCP接口");
}
} catch (Exception e) {
throw new RenException("声纹接口验证失败:" + e.getMessage());
}
}
}
@@ -86,10 +86,14 @@ public class WebSocketClientManager implements Closeable {
if (sess == null || !sess.isOpen()) {
throw new IOException("握手失败或会话未打开");
}
// 设置缓冲区
sess.setTextMessageSizeLimit(b.bufferSize);
sess.setBinaryMessageSizeLimit(b.bufferSize);
ws.session = sess;
return ws;
}
/**
* 发送 Text
*/
@@ -137,6 +141,37 @@ public class WebSocketClientManager implements Closeable {
return collected;
}
private <T> List<T> listenerCustomWithoutClose(
BlockingQueue<T> queue,
Predicate<T> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
List<T> collected = new ArrayList<>();
long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
while (true) {
if (errorFuture.isDone()) {
errorFuture.get();
}
long remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
throw new TimeoutException("等待批量消息超时");
}
T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
if (msg == null) {
throw new TimeoutException("等待批量消息超时");
}
collected.add(msg);
if (predicate.test(msg)) {
break;
}
}
// 不调用 close(),保持连接开放
return collected;
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
*
@@ -147,6 +182,17 @@ public class WebSocketClientManager implements Closeable {
return listenerCustom(textMessageQueue, predicate);
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
* 不自动关闭连接,适用于需要在同一连接上发送多个消息的场景
*
* @return 返回监听期间的所有消息列表
*/
public List<String> listenerWithoutClose(Predicate<String> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustomWithoutClose(textMessageQueue, predicate);
}
public List<byte[]> listenerBinary(Predicate<byte[]> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustom(binaryMessageQueue, predicate);
@@ -266,10 +312,11 @@ public class WebSocketClientManager implements Closeable {
if (stopWatch.isRunning()) {
stopWatch.stop();
}
log.info("ws连接关闭, 目标URI: {}, 关闭时间: {}, 连接总时长: {}s",
log.info("ws连接关闭, 目标URI: {}, 关闭时间: {}, 连接总时长: {}s,断开原因:{}",
targetUri, DateUtils.getDateTimeNow(DateUtils.DATE_TIME_MILLIS_PATTERN),
DateUtils.millsToSecond(stopWatch.getTotalTimeMillis()));
DateUtils.millsToSecond(stopWatch.getTotalTimeMillis()),status);
}
}
public static class Builder {
@@ -279,6 +326,7 @@ public class WebSocketClientManager implements Closeable {
private long maxSessionDuration = 5; // 最大连线时间,默认5秒
private TimeUnit maxSessionDurationUnit = TimeUnit.SECONDS; // 最大连线时间单位
private int queueCapacity = 100; // 消息队列容量
private int bufferSize = 8 * 1024; //默认 8kb
private WebSocketHttpHeaders headers; // 请求头
/**
@@ -310,6 +358,10 @@ public class WebSocketClientManager implements Closeable {
this.queueCapacity = c;
return this;
}
public Builder bufferSize(int c) {
this.bufferSize = c;
return this;
}
public WebSocketClientManager build()
throws InterruptedException, ExecutionException, TimeoutException, IOException {
@@ -0,0 +1,4 @@
-- 添加声纹接口地址参数配置
delete from `sys_params` where id = 114;
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
VALUES (114, 'server.voice_print', 'null', 'string', 1, '声纹接口地址');
@@ -0,0 +1,12 @@
DROP TABLE IF EXISTS ai_agent_voice_print;
create table ai_agent_voice_print (
id varchar(32) NOT NULL COMMENT '声纹ID',
agent_id varchar(32) NOT NULL COMMENT '关联的智能体ID',
source_name varchar(50) NOT NULL COMMENT '声纹来源的人的姓名',
introduce varchar(200) COMMENT '描述声纹来源的这个人',
create_date DATETIME COMMENT '创建时间',
creator bigint COMMENT '创建者',
update_date DATETIME COMMENT '修改时间',
updater bigint COMMENT '修改者',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体声纹表'
@@ -0,0 +1,26 @@
-- 添加阿里云流式ASR供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_AliyunStreamASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_AliyunStreamASR', 'ASR', 'aliyun_stream', '阿里云语音识别(流式)', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"服务地址","type":"string"},{"key":"max_sentence_silence","label":"断句检测时间","type":"number"},{"key":"output_dir","label":"输出目录","type":"string"}]', 6, 1, NOW(), 1, NOW());
-- 添加阿里云流式ASR模型配置
delete from `ai_model_config` where id = 'ASR_AliyunStreamASR';
INSERT INTO `ai_model_config` VALUES ('ASR_AliyunStreamASR', 'ASR', 'AliyunStreamASR', '阿里云语音识别(流式)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-shanghai.aliyuncs.com\", \"max_sentence_silence\": 800, \"output_dir\": \"tmp/\"}', NULL, NULL, 8, NULL, NULL, NULL, NULL);
-- 更新阿里云流式ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://nls-portal.console.aliyun.com/',
`remark` = '阿里云流式ASR配置说明:
1. 阿里云ASR和阿里云(流式)ASR的区别是:阿里云ASR是一次性识别,阿里云(流式)ASR是实时流式识别
2. 流式ASR具有更低的延迟和更好的实时性,适合语音交互场景
3. 需要在阿里云智能语音交互控制台创建应用并获取认证信息
4. 支持中文实时语音识别,支持标点符号预测和逆文本规范化
5. 需要网络连接,输出文件保存在tmp/目录
申请步骤:
1. 访问 https://nls-portal.console.aliyun.com/ 开通智能语音交互服务
2. 访问 https://nls-portal.console.aliyun.com/applist 创建项目并获取appkey
3. 访问 https://nls-portal.console.aliyun.com/overview 获取临时token(或配置access_key_id和access_key_secret自动获取)
4. 如需动态token管理,建议配置access_key_id和access_key_secret
5. max_sentence_silence参数控制断句检测时间(毫秒),默认800ms
如需了解更多参数配置,请参考:https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-recognition
' WHERE `id` = 'ASR_AliyunStreamASR';
@@ -0,0 +1,56 @@
-- 添加阿里云流式TTS供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_AliyunStreamTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_AliyunStreamTTS', 'TTS', 'aliyun_stream', '阿里云语音合成(流式)', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"服务地址","type":"string"},{"key":"voice","label":"默认音色","type":"string"},{"key":"format","label":"音频格式","type":"string"},{"key":"sample_rate","label":"采样率","type":"number"},{"key":"volume","label":"音量","type":"number"},{"key":"speech_rate","label":"语速","type":"number"},{"key":"pitch_rate","label":"音调","type":"number"},{"key":"output_dir","label":"输出目录","type":"string"}]', 15, 1, NOW(), 1, NOW());
-- 添加阿里云流式TTS模型配置
delete from `ai_model_config` where id = 'TTS_AliyunStreamTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_AliyunStreamTTS', 'TTS', 'AliyunStreamTTS', '阿里云语音合成(流式)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-beijing.aliyuncs.com\", \"voice\": \"longxiaochun\", \"format\": \"pcm\", \"sample_rate\": 16000, \"volume\": 50, \"speech_rate\": 0, \"pitch_rate\": 0, \"output_dir\": \"tmp/\"}', NULL, NULL, 18, NULL, NULL, NULL, NULL);
-- 更新阿里云流式TTS配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://nls-portal.console.aliyun.com/',
`remark` = '阿里云流式TTS配置说明:
1. 阿里云TTS和阿里云(流式)TTS的区别是:阿里云TTS是一次性合成,阿里云(流式)TTS是实时流式合成
2. 流式TTS具有更低的延迟和更好的实时性,适合语音交互场景
3. 需要在阿里云智能语音交互控制台创建应用并获取认证信息
4. 支持CosyVoice大模型音色,音质更加自然
5. 支持实时调节音量、语速、音调等参数
申请步骤:
1. 访问 https://nls-portal.console.aliyun.com/ 开通智能语音交互服务
2. 访问 https://nls-portal.console.aliyun.com/applist 创建项目并获取appkey
3. 访问 https://nls-portal.console.aliyun.com/overview 获取临时token(或配置access_key_id和access_key_secret自动获取)
4. 如需动态token管理,建议配置access_key_id和access_key_secret
5. 可选择北京、上海等不同地域的服务器以优化延迟
6. voice参数支持CosyVoice大模型音色,如longxiaochun、longyueyue等
如需了解更多参数配置,请参考:https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-synthesis
' WHERE `id` = 'TTS_AliyunStreamTTS';
-- 添加阿里云流式TTS音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_AliyunStreamTTS';
-- 温柔女声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0001', 'TTS_AliyunStreamTTS', '龙小淳-温柔姐姐', 'longxiaochun', '中文及中英文混合', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0002', 'TTS_AliyunStreamTTS', '龙小夏-温柔女声', 'longxiaoxia', '中文及中英文混合', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0003', 'TTS_AliyunStreamTTS', '龙玫-温柔女声', 'longmei', '中文及中英文混合', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0004', 'TTS_AliyunStreamTTS', '龙瑰-温柔女声', 'longgui', '中文及中英文混合', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
-- 御姐女声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0005', 'TTS_AliyunStreamTTS', '龙玉-御姐女声', 'longyu', '中文及中英文混合', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0006', 'TTS_AliyunStreamTTS', '龙娇-御姐女声', 'longjiao', '中文及中英文混合', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
-- 男声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0007', 'TTS_AliyunStreamTTS', '龙臣-译制片男声', 'longchen', '中文及中英文混合', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0008', 'TTS_AliyunStreamTTS', '龙修-青年男声', 'longxiu', '中文及中英文混合', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0009', 'TTS_AliyunStreamTTS', '龙橙-阳光男声', 'longcheng', '中文及中英文混合', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0010', 'TTS_AliyunStreamTTS', '龙哲-成熟男声', 'longzhe', '中文及中英文混合', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
-- 专业播报系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0011', 'TTS_AliyunStreamTTS', 'Bella2.0-新闻女声', 'loongbella', '中文及中英文混合', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0012', 'TTS_AliyunStreamTTS', 'Stella2.0-飒爽女声', 'loongstella', '中文及中英文混合', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0013', 'TTS_AliyunStreamTTS', '龙书-新闻男声', 'longshu', '中文及中英文混合', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0014', 'TTS_AliyunStreamTTS', '龙婧-严肃女声', 'longjing', '中文及中英文混合', NULL, NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
-- 特色音色系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0015', 'TTS_AliyunStreamTTS', '龙奇-活泼童声', 'longqi', '中文及中英文混合', NULL, NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0016', 'TTS_AliyunStreamTTS', '龙华-活泼女童', 'longhua', '中文及中英文混合', NULL, NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0017', 'TTS_AliyunStreamTTS', '龙无-无厘头男声', 'longwu', '中文及中英文混合', NULL, NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0018', 'TTS_AliyunStreamTTS', '龙大锤-幽默男声', 'longdachui', '中文及中英文混合', NULL, NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
-- 粤语系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0019', 'TTS_AliyunStreamTTS', '龙嘉怡-粤语女声', 'longjiayi', '粤语及粤英混合', NULL, NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0020', 'TTS_AliyunStreamTTS', '龙桃-粤语女声', 'longtao', '粤语及粤英混合', NULL, NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
@@ -0,0 +1,3 @@
-- 智能体声纹添加新字段
ALTER TABLE ai_agent_voice_print
ADD COLUMN audio_id VARCHAR(32) NOT NULL COMMENT '音频ID';
@@ -0,0 +1,38 @@
-- OpenAI ASR模型供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_OpenaiASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_OpenaiASR', 'ASR', 'openai', 'OpenAI语音识别', '[{"key": "base_url", "type": "string", "label": "基础URL"}, {"key": "model_name", "type": "string", "label": "模型名称"}, {"key": "api_key", "type": "string", "label": "API密钥"}, {"key": "output_dir", "type": "string", "label": "输出目录"}]', 9, 1, NOW(), 1, NOW());
-- OpenAI ASR模型配置
delete from `ai_model_config` where id = 'ASR_OpenaiASR';
INSERT INTO `ai_model_config` VALUES ('ASR_OpenaiASR', 'ASR', 'OpenaiASR', 'OpenAI语音识别', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.openai.com/v1/audio/transcriptions\", \"model_name\": \"gpt-4o-mini-transcribe\", \"output_dir\": \"tmp/\"}', NULL, NULL, 9, NULL, NULL, NULL, NULL);
-- groq ASR模型配置
delete from `ai_model_config` where id = 'ASR_GroqASR';
INSERT INTO `ai_model_config` VALUES ('ASR_GroqASR', 'ASR', 'GroqASR', 'Groq语音识别', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.groq.com/openai/v1/audio/transcriptions\", \"model_name\": \"whisper-large-v3-turbo\", \"output_dir\": \"tmp/\"}', NULL, NULL, 10, NULL, NULL, NULL, NULL);
-- 更新OpenAI ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://platform.openai.com/docs/api-reference/audio/createTranscription',
`remark` = 'OpenAI ASR配置说明:
1. 需要在OpenAI开放平台创建组织并获取api_key
2. 支持中、英、日、韩等多种语音识别,具体参考文档https://platform.openai.com/docs/guides/speech-to-text
3. 需要网络连接
4. 输出文件保存在tmp/目录
申请步骤:
**OpenAi ASR申请步骤:**
1.登录OpenAI Platform。https://auth.openai.com/log-in
2.创建api-key https://platform.openai.com/settings/organization/api-keys
3.模型可以选择gpt-4o-transcribe或GPT-4o mini Transcribe
' WHERE `id` = 'ASR_OpenaiASR';
-- 更新Groq ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://console.groq.com/docs/speech-to-text',
`remark` = 'Groq ASR配置说明:
1.登录groq Console。https://console.groq.com/home
2.创建api-key https://console.groq.com/keys
3.模型可以选择whisper-large-v3-turbo或whisper-large-v3distil-whisper-large-v3-en仅支持英语转录)
' WHERE `id` = 'ASR_GroqASR';
@@ -0,0 +1,32 @@
-- 添加Index-TTS-vLLM流式TTS供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_IndexStreamTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_IndexStreamTTS', 'TTS', 'index_stream', 'Index-TTS-vLLM流式语音合成', '[{"key":"api_url","label":"API服务地址","type":"string"},{"key":"voice","label":"默认音色","type":"string"},{"key":"audio_format","label":"音频格式","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]', 16, 1, NOW(), 1, NOW());
-- 添加Index-TTS-vLLM流式TTS模型配置
delete from `ai_model_config` where id = 'TTS_IndexStreamTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_IndexStreamTTS', 'TTS', 'IndexStreamTTS', 'Index-TTS-vLLM流式语音合成', 0, 1, '{\"type\": \"index_stream\", \"api_url\": \"http://127.0.0.1:11996/tts\", \"voice\": \"jay_klee\", \"audio_format\": \"pcm\", \"output_dir\": \"tmp/\"}', NULL, NULL, 19, NULL, NULL, NULL, NULL);
-- 更新Index-TTS-vLLM流式TTS配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://github.com/Ksuriuri/index-tts-vllm',
`remark` = 'Index-TTS-vLLM流式TTS配置说明:
1. Index-TTS-vLLM是基于Index-TTS项目的vLLM推理服务,提供流式语音合成功能
2. 支持多种音色,音质自然,适合各种语音交互场景
3. 需要先部署Index-TTS-vLLM服务,然后配置API地址
4. 支持实时流式合成,具有较低的延迟
5. 支持自定义音色,可在项目assets文件夹下注册新音色
部署步骤:
1. 克隆项目:git clone https://github.com/Ksuriuri/index-tts-vllm.git
2. 安装依赖:pip install -r requirements.txt
3. 启动服务:python app.py
4. 服务默认运行在 http://127.0.0.1:11996
5. 如需其他音色,可到项目assets文件夹下注册
6. 支持多种音频格式:pcm、wav、mp3等
如需了解更多配置,请参考:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md
' WHERE `id` = 'TTS_IndexStreamTTS';
-- 添加Index-TTS-vLLM流式TTS音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_IndexStreamTTS';
-- 默认音色
INSERT INTO `ai_tts_voice` VALUES ('TTS_IndexStreamTTS_0001', 'TTS_IndexStreamTTS', 'Jay Klee', 'jay_klee', '中文及中英文混合', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
@@ -0,0 +1,16 @@
-- 更新HuoshanDoubleStreamTTS供应器增加语速,音调等配置
UPDATE `ai_model_provider`
SET fields = '[{"key": "ws_url", "type": "string", "label": "WebSocket地址"}, {"key": "appid", "type": "string", "label": "应用ID"}, {"key": "access_token", "type": "string", "label": "访问令牌"}, {"key": "resource_id", "type": "string", "label": "资源ID"}, {"key": "speaker", "type": "string", "label": "默认音色"}, {"key": "speech_rate", "type": "number", "label": "语速(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "音量(-50~100)"}, {"key": "pitch", "type": "number", "label": "音高(-12~12)"}]'
WHERE id = 'SYSTEM_TTS_HSDSTTS';
UPDATE `ai_model_config` SET
`doc_link` = 'https://console.volcengine.com/speech/service/10007',
`remark` = '火山引擎语音合成服务配置说明:
1. 访问 https://www.volcengine.com/ 注册并开通火山引擎账号
2. 访问 https://console.volcengine.com/speech/service/10007 开通语音合成大模型,购买音色
3. 在页面底部获取appid和access_token
5. 资源ID固定为:volc.service_type.10029(大模型语音合成及混音)
6. 语速:-50~100,可不填,正常默认值0,可填-50~100
7. 音量:-50~100,可不填,正常默认值0,可填-50~100
8. 音高:-12~12,可不填,正常默认值0,可填-12~12
9. 填入配置文件中' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
@@ -0,0 +1,26 @@
-- 添加 paddle_speech 流式 TTS 供应器
DELETE FROM `ai_model_provider` WHERE id = 'SYSTEM_TTS_PaddleSpeechTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`)
VALUES ('SYSTEM_TTS_PaddleSpeechTTS', 'TTS', 'paddle_speech', 'PaddleSpeechTTS',
'[{"key":"protocol","label":"协议类型","type":"string","options":["websocket","http"]},{"key":"url","label":"服务地址","type":"string"},{"key":"spk_id","label":"音色","type":"int"},{"key":"sample_rate","label":"采样率","type":"float"},{"key":"speed","label":"语速","type":"float"},{"key":"volume","label":"音量","type":"float"},{"key":"save_path","label":"保存路径","type":"string"}]',
17, 1, NOW(), 1, NOW());
-- 添加 paddle_speech 流式 TTS 模型配置
DELETE FROM `ai_model_config` WHERE id = 'TTS_PaddleSpeechTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_PaddleSpeechTTS', 'TTS', 'PaddleSpeechTTS', 'PaddleSpeechTTS', 0, 1,
'{"type": "paddle_speech", "protocol": "websocket", "url": "ws://127.0.0.1:8092/paddlespeech/tts/streaming", "spk_id": "0", "sample_rate": 24000, "speed": 1.0, "volume": 1.0, "save_path": "./streaming_tts.wav"}',
NULL, NULL, 20, NULL, NULL, NULL, NULL);
-- 更新 PaddleSpeechTTS 配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://github.com/PaddlePaddle/PaddleSpeech',
`remark` = 'PaddleSpeechTTS 配置说明:
1. PaddleSpeech 是百度飞桨开源的语音合成工具,支持本地离线部署和模型训练。paddlepaddle百度飞浆框架地址:https://www.paddlepaddle.org.cn/
2. 支持 WebSocket 和 HTTP 协议,默认使用 WebSocket 进行流式传输(参考部署文档:https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/paddlespeech-deploy.md)。
3. 使用前要在本地部署 paddlespeech 服务,服务默认运行在 ws://127.0.0.1:8092/paddlespeech/tts/streaming
4. 支持自定义发音人、语速、音量和采样率。
' WHERE `id` = 'TTS_PaddleSpeechTTS';
-- 删除旧音色并添加默认音色
DELETE FROM `ai_tts_voice` WHERE tts_model_id = 'TTS_PaddleSpeechTTS';
INSERT INTO `ai_tts_voice` VALUES ('TTS_PaddleSpeechTTS_0000', 'TTS_PaddleSpeechTTS', '默认', '0', '中文', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
@@ -0,0 +1,24 @@
-- VOSK ASR模型供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_VoskASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_VoskASR', 'ASR', 'vosk', 'VOSK离线语音识别', '[{"key": "model_path", "type": "string", "label": "模型路径"}, {"key": "output_dir", "type": "string", "label": "输出目录"}]', 11, 1, NOW(), 1, NOW());
-- VOSK ASR模型配置
delete from `ai_model_config` where id = 'ASR_VoskASR';
INSERT INTO `ai_model_config` VALUES ('ASR_VoskASR', 'ASR', 'VoskASR', 'VOSK离线语音识别', 0, 1, '{\"type\": \"vosk\", \"model_path\": \"\", \"output_dir\": \"tmp/\"}', NULL, NULL, 11, NULL, NULL, NULL, NULL);
-- 更新VOSK ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://alphacephei.com/vosk/',
`remark` = 'VOSK ASR配置说明:
1. VOSK是一个离线语音识别库,支持多种语言
2. 需要先下载模型文件:https://alphacephei.com/vosk/models
3. 中文模型推荐使用vosk-model-small-cn-0.22或vosk-model-cn-0.22
4. 完全离线运行,无需网络连接
5. 输出文件保存在tmp/目录
使用步骤:
1. 访问 https://alphacephei.com/vosk/models 下载中文模型
2. 解压模型文件到项目目录下的models/vosk/文件夹
3. 在配置中指定正确的模型路径
4. 注意:VOSK中文模型输出不带标点符号,词与词之间会有空格
' WHERE `id` = 'ASR_VoskASR';
@@ -0,0 +1,45 @@
-- 添加 MinimaxHTTPStream 流式 TTS 供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_MinimaxStreamTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_MinimaxStreamTTS', 'TTS', 'minimax_httpstream', 'Minimax流式语音合成', '[{"key":"group_id","label":"组ID","type":"string"},{"key":"api_key","label":"API密钥","type":"string"},{"key":"model","label":"模型","type":"string"},{"key":"voice_id","label":"音色ID","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"voice_setting","label":"音色设置","type":"dict","dict_name":"voice_setting"},{"key":"pronunciation_dict","label":"发音字典","type":"dict","dict_name":"pronunciation_dict"},{"key":"audio_setting","label":"音频设置","type":"dict","dict_name":"audio_setting"},{"key":"timber_weights","label":"音色权重","type":"string"}]', 18, 1, NOW(), 1, NOW());
-- 添加Minimax流式TTS模型配置
delete from `ai_model_config` where id = 'TTS_MinimaxStreamTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_MinimaxStreamTTS', 'TTS', 'MinimaxStreamTTS', 'Minimax流式语音合成', 0, 1, '{"type": "minimax_httpstream", "group_id": "", "api_key": "", "model": "speech-01-turbo", "voice_id": "female-shaonv", "output_dir": "tmp/", "voice_setting": {"speed": 1, "vol": 1, "pitch": 0, "emotion": "happy"}, "pronunciation_dict": {"tone": ["处理/(chu3)(li3)", "危险/dangerous"]}, "audio_setting": {"sample_rate": 24000, "bitrate": 128000, "format": "pcm", "channel": 1}}', NULL, NULL, 21, NULL, NULL, NULL, NULL);
-- 更新Minimax流式TTS配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://platform.minimaxi.com/',
`remark` = 'Minimax流式TTS配置说明:
1. 需要先申请Minimax API Key
2. 需要填写Group ID
3. 支持多种音色设置和音频参数调整
4. 支持实时流式合成,具有较低的延迟
5. 支持自定义发音字典和音色权重
6. 隐藏参数配置:声音设定(voice_setting)、发音字典(pronunciation_dict)、音色权重(timber_weights)
- 语速(speed): 范围[0.5,2],默认1.0,取值越大语速越快
- 音量(vol): 范围(0,10],默认1.0,取值越大音量越高
- 音调(pitch): 范围[-12,12],默认0,取值需为整数
- 情绪(emotion): 控制合成语音的情绪,支持7种值:["happy", "sad", "angry", "fearful", "disgusted", "surprised", "calm"],该参数仅对 speech-2.5-hd-preview、speech-2.5-turbo-preview、speech-02-hd、speech-02-turbo、speech-01-turbo、speech-01-hd 生效
- timbre_weights与voice_id二选一必填
- voice_id(请求的音色id,须和weight参数同步填写)
- weight(权重,最多支持4种音色混合。范围[1,100])
' WHERE `id` = 'TTS_MinimaxStreamTTS';
-- 添加Minimax流式TTS音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_MinimaxStreamTTS';
-- 默认音色
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0001', 'TTS_MinimaxStreamTTS', '少女音', 'female-shaonv', '中文', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0002', 'TTS_MinimaxStreamTTS', '成熟女声', 'female-chengshu', '中文', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0003', 'TTS_MinimaxStreamTTS', '霸道少爷', 'badao_shaoye', '中文', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0004', 'TTS_MinimaxStreamTTS', '病娇弟弟', 'bingjiao_didi', '中文', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0005', 'TTS_MinimaxStreamTTS', '纯真学弟', 'chunzhen_xuedi', '中文', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0006', 'TTS_MinimaxStreamTTS', '冷淡学长', 'lengdan_xiongzhang', '中文', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0007', 'TTS_MinimaxStreamTTS', '甜美小玲', 'tianxin_xiaoling', '中文', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0008', 'TTS_MinimaxStreamTTS', '俏皮萌妹', 'qiaopi_mengmei', '中文', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0009', 'TTS_MinimaxStreamTTS', '妩媚御姐', 'wumei_yujie', '中文', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0010', 'TTS_MinimaxStreamTTS', '嗲嗲学妹', 'diadia_xuemei', '中文', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0011', 'TTS_MinimaxStreamTTS', '淡雅学姐', 'danya_xuejie', '中文', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0012', 'TTS_MinimaxStreamTTS', 'Santa Claus', 'Santa_Claus', '中文', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_MinimaxStreamTTS_0013', 'TTS_MinimaxStreamTTS', 'Grinch', 'Grinch', '中文', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
@@ -0,0 +1,8 @@
delete from `sys_params` where param_code = 'server.mqtt_gateway';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (116, 'server.mqtt_gateway', 'null', 'string', 1, 'mqtt gateway 配置');
delete from `sys_params` where param_code = 'server.mqtt_signature_key';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (117, 'server.mqtt_signature_key', 'null', 'string', 1, 'mqtt 密钥 配置');
delete from `sys_params` where param_code = 'server.udp_gateway';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (118, 'server.udp_gateway', 'null', 'string', 1, 'udp gateway 配置');
@@ -0,0 +1,4 @@
-- 添加声纹识别相似度阈值参数配置
delete from `sys_params` where id = 115;
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
VALUES (115, 'server.voiceprint_similarity_threshold', '0.4', 'string', 1, '声纹识别相似度阈值,范围0.0-1.0,默认0.4,数值越高越严格');
@@ -0,0 +1,10 @@
-- 删除非流式MiniMax TTS配置,保留流式版本
-- 删除旧的非流式MiniMax TTS模型配置
DELETE FROM `ai_model_config` WHERE `id` = 'TTS_MinimaxTTS';
-- 删除旧的非流式MiniMax TTS供应器配置
DELETE FROM `ai_model_provider` WHERE `id` = 'SYSTEM_TTS_minimax';
-- 删除旧的非流式MiniMax TTS音色配置
DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_MinimaxTTS';
@@ -0,0 +1,16 @@
-- 添加通义千问Qwen3-ASR-Flash语音识别服务配置
delete from `ai_model_provider` where id = 'SYSTEM_ASR_Qwen3Flash';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_Qwen3Flash', 'ASR', 'qwen3_asr_flash', 'Qwen3-ASR-Flash语音识别', '[{"key":"api_key","label":"API密钥","type":"password"},{"key":"base_url","label":"服务地址","type":"string"},{"key":"model_name","label":"模型名称","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]', 17, 1, NOW(), 1, NOW());
delete from `ai_model_config` where id = 'ASR_Qwen3Flash';
INSERT INTO `ai_model_config` VALUES ('ASR_Qwen3Flash', 'ASR', 'Qwen3-ASR-Flash', '通义千问语音识别服务', 0, 1, '{"type": "qwen3_asr_flash", "api_key": "", "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model_name": "qwen3-asr-flash", "output_dir": "tmp/", "enable_lid": true, "enable_itn": true}', 'https://help.aliyun.com/zh/bailian/', '支持多语言识别、歌唱识别、噪声拒识功能', 20, NULL, NULL, NULL, NULL);
-- 更新Qwen3-ASR-Flash模型配置的说明文档
UPDATE `ai_model_config` SET
`doc_link` = 'https://bailian.console.aliyun.com/?apiKey=1&tab=doc#/doc/?type=model&url=2979031',
`remark` = '通义千问Qwen3-ASR-Flash配置说明:
1. 登录阿里云百炼平台https://bailian.console.aliyun.com/
2. 创建API-KEY https://bailian.console.aliyun.com/#/api-key
3.Qwen3-ASR-Flash基于通义千问多模态基座,支持多语言识别、歌唱识别、噪声拒识等功能
' WHERE `id` = 'ASR_Qwen3Flash';
@@ -239,4 +239,109 @@ databaseChangeLog:
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202506261637.sql
path: classpath:db/changelog/202506261637.sql
- changeSet:
id: 202507101203
author: luruxian
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507101203.sql
- changeSet:
id: 202507071130
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507071130.sql
- changeSet:
id: 202507071530
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507071530.sql
- changeSet:
id: 202507031602
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507031602.sql
- changeSet:
id: 202507041018
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507041018.sql
- changeSet:
id: 202507081646
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507081646.sql
- changeSet:
id: 202508081701
author: hrz
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508081701.sql
- changeSet:
id: 202508111734
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508111734.sql
- changeSet:
id: 202508131557
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508131557.sql
- changeSet:
id: 202508271113
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508271113.sql
- changeSet:
id: 202509051745
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509051745.sql
- changeSet:
id: 202509081140
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509081140.sql
- changeSet:
id: 202509091042
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509091042.sql
- changeSet:
id: 202509091633
author: fyb
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509091633.sql
- changeSet:
id: 202509080922
author: fyb
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509080922.sql
@@ -4,11 +4,11 @@
<mapper namespace="xiaozhi.modules.security.dao.SysUserTokenDao">
<select id="getByToken" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
select * from sys_user_token where token = #{value}
select * from sys_user_token where token = #{token}
</select>
<select id="getByUserId" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
select * from sys_user_token where user_id = #{value}
select * from sys_user_token where user_id = #{userId}
</select>
<update id="logout">
@@ -5,7 +5,7 @@
<!-- 根据参数编码,查询value -->
<select id="getValueByCode" resultType="String">
select param_value from sys_params where param_code = #{value}
select param_value from sys_params where param_code = #{paramCode}
</select>
<!-- 获取参数编码列表 -->
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
}
+13
View File
@@ -0,0 +1,13 @@
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+44
View File
@@ -0,0 +1,44 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.hbuilderx
.stylelintcache
.eslintcache
docs/.vitepress/dist
docs/.vitepress/cache
src/types
# lock 文件还是不要了,我主要的版本写死就好了
# pnpm-lock.yaml
# package-lock.json
# TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
# `git rm -r --cached .` 然后提交 commit 即可。
# git rm -r --cached file1 file2 ## 针对某些文件
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
# git rm -r --cached . ## 针对所有文件
# 更新 uni-app 官方版本
# npx @dcloudio/uvm@latest
+8
View File
@@ -0,0 +1,8 @@
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
ignore-workspace-root-check=true
install-workspace-root=true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Junsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+170
View File
@@ -0,0 +1,170 @@
## 智控台移动版(manager-mobile
基于 uni-app v3 + Vue 3 + Vite 的跨端移动管理端,支持 AppAndroid & iOS)和微信小程序。
### 平台兼容性
| H5 | iOS | Android | 微信小程序 |
| -- | --- | ------- | ---------- |
| √ | √ | √ | √ |
提示:不同 UI 组件在不同平台的适配度略有差异,请以对应组件库文档为准。
### 开发环境要求
- Node >= 18
- pnpm >= 7.30(建议使用项目中声明的 `pnpm@10.x`
- 可选:HBuilderX(App 调试/打包)、微信开发者工具(微信小程序)
### 快速开始
1) 配置环境变量
- 复制 `env/.env.example``env/.env.development`
- 根据实际情况修改配置项(特别是 `VITE_SERVER_BASEURL``VITE_UNI_APPID``VITE_WX_APPID`
2) 安装依赖
```bash
pnpm i
```
3) 本地开发(热更新)
- h5: `pnpm dev:h5`,然后观察启动日志显示的ip端口号
- 微信小程序:`pnpm dev:mp``pnpm dev:mp-weixin`,然后用微信开发者工具导入 `dist/dev/mp-weixin`
- App:用 HBuilderX 导入 `manager-mobile`,然后参考下面的教程就能运行了
### 环境变量与配置
项目使用自定义 `env` 目录存放环境文件,按 Vite 规范命名:`.env.development``.env.production` 等。
关键变量(部分):
- VITE_APP_TITLE:应用名称(写入 `manifest.config.ts`
- VITE_UNI_APPIDuni-app 应用 appidApp
- VITE_WX_APPID:微信小程序 appidmp-weixin
- VITE_FALLBACK_LOCALE:默认语言,如 `zh-Hans`
- VITE_SERVER_BASEURL:服务端基础地址(HTTP 请求 baseURL
- VITE_DELETE_CONSOLE:构建时是否移除 console`true`/`false`
- VITE_SHOW_SOURCEMAP:是否生成 sourcemap(默认关闭)
- VITE_LOGIN_URL:未登录跳转的登录页路径(路由拦截器使用)
示例(`env/.env.development`):
```env
VITE_APP_TITLE=小智
VITE_FALLBACK_LOCALE=zh-Hans
VITE_UNI_APPID=
VITE_WX_APPID=
VITE_SERVER_BASEURL=http://localhost:8080
VITE_DELETE_CONSOLE=false
VITE_SHOW_SOURCEMAP=false
VITE_LOGIN_URL=/pages/login/index
```
说明:
- `manifest.config.ts` 会从 `env` 读取标题、appid、语言等配置。
### 重要注意事项
⚠️ **部署前必须修改的配置项:**
1. **应用 ID 配置**
- `VITE_UNI_APPID`:需要在 [DCloud 开发者中心](https://dev.dcloud.net.cn/) 创建应用并获取 AppID
- `VITE_WX_APPID`:需要在 [微信公众平台](https://mp.weixin.qq.com/) 注册小程序并获取 AppID
2. **服务端地址**
- `VITE_SERVER_BASEURL`:修改为您的实际服务端地址
3. **应用信息**
- `VITE_APP_TITLE`:修改为您的应用名称
- 更新 `src/static/logo.png` 等图标资源
4. **其他配置**
- 检查 `manifest.config.ts` 中的应用配置信息
- 根据需要修改 `src/layouts/fg-tabbar/tabbarList.ts` 中的 tabbar 配置
### 详细操作指南
#### 1. 获取 uni-app AppID
![生成AppID](../../docs/images/manager-mobile/生成appid.png)
- 复制生成的 AppID 到环境变量 `VITE_UNI_APPID`
#### 2. 本地运行步骤
![本地运行](../../docs/images/manager-mobile/本地运行.png)
**App 本地调试:**
1. 用 HBuilderX 导入 `manager-mobile` 目录
2. 重新识别项目
3. 连接手机或使用模拟器进行真机调试
**项目识别问题解决:**
![重新识别项目](../../docs/images/manager-mobile/重新识别项目.png)
如果 HBuilderX 无法正确识别项目类型:
- 在项目根目录右键选择"重新识别项目类型"
- 确保项目被识别为 "uni-app" 项目
### 路由与鉴权
-`src/main.ts` 中注册了路由拦截插件 `routeInterceptor`
- 黑名单拦截:仅对配置为需要登录的页面进行校验(来源 `@/utils``getNeedLoginPages`)。
- 登录判断:基于用户信息(`pinia``useUserStore`),未登录将跳转到 `VITE_LOGIN_URL`,并附带重定向回原页面的参数。
### 网络请求
- 基于 `alova` + `@alova/adapter-uniapp`,统一在 `src/http/request/alova.ts` 创建实例。
- `baseURL` 读取环境配置(`getEnvBaseUrl`),可通过 `method.config.meta.domain` 动态切换域名。
- 认证:默认从本地 `token``uni.getStorageSync('token')`)注入 `Authorization` 头,缺失则重定向登录。
- 响应:统一处理 `statusCode !== 200` 的 HTTP 错误与业务 `code !== 0` 的错误;`401` 会清除 token 并跳转登录。
### 构建与发布
**微信小程序:**
1. 确保已配置正确的 `VITE_WX_APPID`
2. 运行 `pnpm build:mp`,产物在 `dist/build/mp-weixin`
3. 用微信开发者工具导入项目目录,并上传代码
4. 在微信公众平台提交审核
**Android & iOS App**
#### 3. App 打包发行步骤
**步骤一:准备打包**
![打包发行步骤1](../../docs/images/manager-mobile/打包发行步骤1.png)
1. 确保已配置正确的 `VITE_UNI_APPID`
2. 运行 `pnpm build:app`,产物在 `dist/build/app`
3. 用 HBuilderX 导入项目目录
4. 在 HBuilderX 中点击"发行" → "原生App-云打包"
**步骤二:配置打包参数**
![打包发行步骤2](../../docs/images/manager-mobile/打包发行步骤2.png)
1. **应用图标和启动图**:上传应用图标和启动页面图片
2. **应用版本号**:设置版本号和版本名称
3. **签名证书**
- Android:上传 keystore 证书文件
- iOS:配置开发者证书和描述文件
4. **包名配置**:设置应用的包名(Bundle ID)
5. **打包类型**:选择测试包或正式包
6. 点击"打包"开始云打包流程
**发布到应用商店:**
- **Android**:将生成的 APK 文件上传到各大 Android 应用市场
- **iOS**:将生成的 IPA 文件通过 App Store Connect 上传到 App Store(需要 Apple 开发者账号)
### 约定与工程化
- 页面与分包:由 `@uni-helper/vite-plugin-uni-pages``pages.config.ts` 统一生成;tabbar 配置在 `src/layouts/fg-tabbar/tabbarList.ts`
- 组件与 hooks 自动导入:见 `vite.config.ts``unplugin-auto-import``@uni-helper/vite-plugin-uni-components`
- 样式:使用 UnoCSS 与 `src/style/index.scss`
- 状态管理:`pinia` + `pinia-plugin-persistedstate`
- 代码规范:内置 `eslint``husky``lint-staged`,提交前自动格式化(`lint-staged`)。
### 常用脚本
```bash
# 开发
pnpm dev:mp # 等价 dev:mp-weixin
# 构建
pnpm build:mp # 等价 build:mp-weixin
# 其他
pnpm type-check
pnpm lint && pnpm lint:fix
```
### License
MIT
+19
View File
@@ -0,0 +1,19 @@
VITE_APP_TITLE = '小智'
VITE_APP_PORT = 9000
VITE_UNI_APPID = '__UNI__36A515E'
VITE_WX_APPID = 'wxa2abb91f64032a2b'
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
VITE_APP_PUBLIC_BASE=/
# 登录页面
VITE_LOGIN_URL = '/pages/login/index'
# 第一个请求地址
VITE_SERVER_BASEURL = 'https://2662r3426b.vicp.fun/xiaozhi'
VITE_UPLOAD_BASEURL = '/otaMag/upload'
# h5是否需要配置代理
VITE_APP_PROXY=true
VITE_APP_PROXY_PREFIX = '/xiaozhi'
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = true
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = false
+4
View File
@@ -0,0 +1,4 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
+43
View File
@@ -0,0 +1,43 @@
import uniHelper from '@uni-helper/eslint-config'
export default uniHelper({
unocss: true,
vue: true,
markdown: false,
ignores: [
'src/uni_modules/',
'dist',
// unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
'auto-import.d.ts',
// vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
'uni-pages.d.ts',
// 插件生成的文件
'src/pages.json',
'src/manifest.json',
// 忽略自动生成文件
'src/service/app/**',
],
rules: {
'no-console': 'off',
'no-unused-vars': 'off',
'vue/no-unused-refs': 'off',
'unused-imports/no-unused-vars': 'off',
'eslint-comments/no-unlimited-disable': 'off',
'jsdoc/check-param-names': 'off',
'jsdoc/require-returns-description': 'off',
'ts/no-empty-object-type': 'off',
'no-extend-native': 'off',
},
formatters: {
/**
* Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
* By default uses Prettier
*/
css: true,
/**
* Format HTML files
* By default uses Prettier
*/
html: true,
},
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

+26
View File
@@ -0,0 +1,26 @@
<!doctype html>
<html build-time="%BUILD_TIME%">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<script>
var coverSupport =
'CSS' in window &&
typeof CSS.supports === 'function' &&
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') +
'" />',
)
</script>
<title>unibest</title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+155
View File
@@ -0,0 +1,155 @@
import path from 'node:path'
import process from 'node:process'
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import { loadEnv } from 'vite'
// 手动解析命令行参数获取 mode
function getMode() {
const args = process.argv.slice(2)
const modeFlagIndex = args.findIndex(arg => arg === '--mode')
return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 默认 development
}
// 获取环境变量的范例
const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
const {
VITE_APP_TITLE,
VITE_UNI_APPID,
VITE_WX_APPID,
VITE_APP_PUBLIC_BASE,
VITE_FALLBACK_LOCALE,
} = env
export default defineManifestConfig({
'name': VITE_APP_TITLE,
'appid': VITE_UNI_APPID,
'description': '',
'versionName': '1.0.0',
'versionCode': '100',
'transformPx': false,
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
'h5': {
router: {
// base: VITE_APP_PUBLIC_BASE,
},
},
/* 5+App特有相关 */
'app-plus': {
usingComponents: true,
nvueStyleCompiler: 'uni-app',
compilerVersion: 3,
compatible: {
ignoreVersion: true,
},
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
minSdkVersion: 30,
targetSdkVersion: 30,
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
'<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>',
'<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>',
],
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {},
/* 图标配置 */
icons: {
android: {
hdpi: 'unpackage/res/icons/72x72.png',
xhdpi: 'unpackage/res/icons/96x96.png',
xxhdpi: 'unpackage/res/icons/144x144.png',
xxxhdpi: 'unpackage/res/icons/192x192.png',
},
ios: {
appstore: 'unpackage/res/icons/1024x1024.png',
ipad: {
'app': 'unpackage/res/icons/76x76.png',
'app@2x': 'unpackage/res/icons/152x152.png',
'notification': 'unpackage/res/icons/20x20.png',
'notification@2x': 'unpackage/res/icons/40x40.png',
'proapp@2x': 'unpackage/res/icons/167x167.png',
'settings': 'unpackage/res/icons/29x29.png',
'settings@2x': 'unpackage/res/icons/58x58.png',
'spotlight': 'unpackage/res/icons/40x40.png',
'spotlight@2x': 'unpackage/res/icons/80x80.png',
},
iphone: {
'app@2x': 'unpackage/res/icons/120x120.png',
'app@3x': 'unpackage/res/icons/180x180.png',
'notification@2x': 'unpackage/res/icons/40x40.png',
'notification@3x': 'unpackage/res/icons/60x60.png',
'settings@2x': 'unpackage/res/icons/58x58.png',
'settings@3x': 'unpackage/res/icons/87x87.png',
'spotlight@2x': 'unpackage/res/icons/80x80.png',
'spotlight@3x': 'unpackage/res/icons/120x120.png',
},
},
},
},
},
/* 快应用特有相关 */
'quickapp': {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
// 是否启用 ES6 转 ES5
es6: true,
minified: true,
},
optimization: {
subPackages: true,
},
usingComponents: true,
// __usePrivacyCheck__: true,
permission: {
'scope.userLocation': {
desc: 'WiFi配网功能需要获取位置权限',
},
},
requiredPrivateInfos: ['getLocation'],
},
'mp-alipay': {
usingComponents: true,
styleIsolation: 'shared',
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
'uniStatistics': {
enable: false,
},
'vueVersion': '3',
})
+161
View File
@@ -0,0 +1,161 @@
{
"name": "xiaozhi-mobile-admin",
"type": "commonjs",
"version": "3.4.0",
"unibest-version": "3.4.0",
"packageManager": "pnpm@10.10.0",
"description": "xiaozhi-esp32-server的移动端管理后台",
"generate-time": "2025-08-04",
"author": {
"name": "huangjunsen",
"zhName": "黄俊森",
"email": "huangjunsen@xiaozhi.com",
"github": "https://github.com/huangjunsen0406",
"gitee": "https://gitee.com/huang-jun-sen"
},
"license": "MIT",
"homepage": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
"repository": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
"bugs": {
"url": "https://github.com/xinnan-tech/xiaozhi-esp32-server/issues"
},
"engines": {
"node": ">=18",
"pnpm": ">=7.30"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
"uvm": "npx @dcloudio/uvm@latest",
"uvm-rm": "node ./scripts/postupgrade.js",
"postuvm": "echo upgrade uni-app success!",
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev": "uni",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp": "uni -p mp-weixin",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp": "uni build -p mp-weixin",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"openapi-ts-request": "openapi-ts",
"prepare": "git init && husky",
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"dependencies": {
"@alova/adapter-uniapp": "^2.0.14",
"@alova/shared": "^1.3.1",
"@dcloudio/uni-app": "3.0.0-4060620250520001",
"@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
"@dcloudio/uni-components": "3.0.0-4060620250520001",
"@dcloudio/uni-h5": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
"@tanstack/vue-query": "^5.62.16",
"abortcontroller-polyfill": "^1.7.8",
"alova": "^3.3.3",
"dayjs": "1.11.10",
"js-cookie": "^3.0.5",
"pinia": "2.0.36",
"pinia-plugin-persistedstate": "3.2.1",
"vue": "^3.4.21",
"wot-design-uni": "^1.9.1",
"z-paging": "2.8.7"
},
"devDependencies": {
"@alova/wormhole": "^1.0.8",
"@antfu/eslint-config": "^4.15.0",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4060620250520001",
"@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
"@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
"@dcloudio/uni-uts-v1": "3.0.0-4060620250520001",
"@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@iconify-json/carbon": "^1.2.4",
"@rollup/rollup-darwin-x64": "^4.28.0",
"@types/node": "^20.17.9",
"@types/wechat-miniprogram": "^3.4.8",
"@uni-helper/eslint-config": "^0.4.0",
"@uni-helper/uni-types": "1.0.0-alpha.3",
"@uni-helper/unocss-preset-uni": "^0.2.11",
"@uni-helper/vite-plugin-uni-components": "0.2.0",
"@uni-helper/vite-plugin-uni-layouts": "0.1.10",
"@uni-helper/vite-plugin-uni-manifest": "0.2.8",
"@uni-helper/vite-plugin-uni-pages": "0.2.28",
"@uni-helper/vite-plugin-uni-platform": "0.0.4",
"@uni-ku/bundle-optimizer": "^1.3.3",
"@unocss/eslint-plugin": "^66.2.3",
"@unocss/preset-legacy-compat": "^0.59.4",
"@vue/runtime-core": "^3.4.21",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.20",
"eslint": "^9.29.0",
"eslint-plugin-format": "^1.0.1",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"openapi-ts-request": "^1.1.2",
"postcss": "^8.4.49",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "1.77.8",
"typescript": "^5.7.2",
"unocss": "65.4.2",
"unplugin-auto-import": "^0.17.8",
"vite": "5.2.8",
"vite-plugin-restart": "^0.4.2",
"vue-tsc": "^2.2.10"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
},
"lint-staged": {
"*": "eslint --fix"
}
}
+23
View File
@@ -0,0 +1,23 @@
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
import { tabBar } from './src/layouts/fg-tabbar/tabbarList'
export default defineUniPages({
globalStyle: {
navigationStyle: 'default',
navigationBarTitleText: '小智',
navigationBarBackgroundColor: '#f8f8f8',
navigationBarTextStyle: 'black',
backgroundColor: '#FFFFFF',
},
easycom: {
autoscan: true,
custom: {
'^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
'z-paging/components/z-paging$1/z-paging$1.vue',
},
},
// tabbar 的配置统一在 "./src/layouts/fg-tabbar/tabbarList.ts" 文件中
tabBar: tabBar as any,
})
@@ -0,0 +1,13 @@
diff --git a/dist/uni-h5.es.js b/dist/uni-h5.es.js
index 7421bad97d94ad34a3d4d94292a9ee9071430662..19c6071ee4036ceb8d1cfa09030e471c002d2cda 100644
--- a/dist/uni-h5.es.js
+++ b/dist/uni-h5.es.js
@@ -23410,7 +23410,7 @@ function useShowTabBar(emit2) {
const tabBar2 = useTabBar();
const showTabBar2 = computed(() => route.meta.isTabBar && tabBar2.shown);
updateCssVar({
- "--tab-bar-height": tabBar2.height
+ "--tab-bar-height": tabBar2?.height || 0
});
return showTabBar2;
}

Some files were not shown because too many files have changed in this diff Show More