mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-23 23:53:55 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d721270bed | ||
|
|
fe2e178b19 | ||
|
|
21337e5046 | ||
|
|
1c8b271e20 | ||
|
|
3d1160b6bb | ||
|
|
086c4a55e6 | ||
|
|
889f2f9a75 | ||
|
|
07b686be14 | ||
|
|
4d6b453485 | ||
|
|
9909045f9d | ||
|
|
b0dc4d88db | ||
|
|
bbf3858735 | ||
|
|
73573e2f12 | ||
|
|
99e136786e | ||
|
|
205be0e4ec | ||
|
|
2e64f45a31 | ||
|
|
818b4f2f6d | ||
|
|
5d72f9f79d | ||
|
|
0ee349df2d | ||
|
|
4dde66339b | ||
|
|
0064f975c2 | ||
|
|
43234f4798 | ||
|
|
48034fc000 | ||
|
|
3fc33e9095 | ||
|
|
da71dce860 | ||
|
|
b7e4408a0f | ||
|
|
4f4f8ca54e | ||
|
|
99c12a0fed | ||
|
|
dcd0ef3f40 | ||
|
|
2f53b921fe | ||
|
|
3051dc8327 | ||
|
|
c313997a61 | ||
|
|
07c0c764d3 | ||
|
|
e339a501aa | ||
|
|
f2c541b4ab | ||
|
|
206af1c074 | ||
|
|
344729c9a6 | ||
|
|
3b1c2d3b1b | ||
|
|
3660af9619 | ||
|
|
4905ba836c | ||
|
|
ce00c862fe | ||
|
|
a50229431b | ||
|
|
275102f5b7 | ||
|
|
ff6286ffcc | ||
|
|
974fe6c3b7 | ||
|
|
b78292b270 | ||
|
|
3559a7018c | ||
|
|
3222d6175d | ||
|
|
28f9f23449 | ||
|
|
5e7d192ebc | ||
|
|
d6a32416d7 | ||
|
|
6a2c01763e | ||
|
|
e1dd272708 | ||
|
|
3e7bc0606a | ||
|
|
9d9ca1475d | ||
|
|
43ddc0135c | ||
|
|
98f907a97b | ||
|
|
b2ddf060c9 | ||
|
|
5e612276db | ||
|
|
cbb293e7fa | ||
|
|
fdb050f6ed | ||
|
|
f83ddf79fa | ||
|
|
95b340a7a3 | ||
|
|
d46ac89e58 | ||
|
|
80b0912f84 | ||
|
|
e0b4431064 | ||
|
|
e3f16f91cf | ||
|
|
5f39e5c308 | ||
|
|
83304c9ff4 | ||
|
|
863cfb575c | ||
|
|
2944040648 | ||
|
|
5f19b9b0d0 | ||
|
|
2948ed091c | ||
|
|
97ee397b54 | ||
|
|
f7fbe7efbe | ||
|
|
ce6b0bd099 | ||
|
|
8643960104 | ||
|
|
b4549ebc59 | ||
|
|
a602cddd05 | ||
|
|
c4278864d1 | ||
|
|
7094548ecf | ||
|
|
363fffada8 | ||
|
|
c2c3eb7106 | ||
|
|
8e19a64f15 | ||
|
|
9042b7e6d7 | ||
|
|
ad1855cfb5 | ||
|
|
efa0d1095d | ||
|
|
a8c13d740b | ||
|
|
7475e9829f | ||
|
|
b7dde1dae2 | ||
|
|
778c4560fd | ||
|
|
fa31096cd8 | ||
|
|
fd1dd39463 | ||
|
|
ecd3e12a41 | ||
|
|
0a8121e849 | ||
|
|
e93f9d7111 | ||
|
|
fdf98985a1 | ||
|
|
6c3609e79e | ||
|
|
a9aa709502 | ||
|
|
1f6a89ada5 | ||
|
|
31a1180b79 | ||
|
|
77b4a416a9 | ||
|
|
da7a6933f5 | ||
|
|
54cac0d2fd | ||
|
|
7fe718c887 | ||
|
|
36eaefb8b0 | ||
|
|
3a61ec84b3 | ||
|
|
c1bf92d243 | ||
|
|
c1959e0795 | ||
|
|
40a6b7434b | ||
|
|
24519a2732 | ||
|
|
c190abe4b1 | ||
|
|
07d5b3cd2a | ||
|
|
81e923b4f7 | ||
|
|
bcb51729a5 | ||
|
|
a731c1d01f | ||
|
|
0bb5a37ace | ||
|
|
a711827996 | ||
|
|
8f854e6f74 | ||
|
|
f1b5afbf5c | ||
|
|
84f6930389 | ||
|
|
f415dd4482 | ||
|
|
927f65e106 | ||
|
|
4b6b3082c7 | ||
|
|
4c550245db | ||
|
|
341ed22370 | ||
|
|
6851ccacf3 | ||
|
|
815af687ea | ||
|
|
e9373b59c8 | ||
|
|
3f3f3fcd57 | ||
|
|
d003e66310 | ||
|
|
e6108ccbbd | ||
|
|
a01f01198c | ||
|
|
cc96eaeb8f | ||
|
|
e25ca3d1d3 | ||
|
|
9c111b2d5e | ||
|
|
436a01fb6e | ||
|
|
9b6620dd4a | ||
|
|
85919e800e | ||
|
|
a2023a65c0 | ||
|
|
619370f36c | ||
|
|
ea6c0e691f | ||
|
|
de0d82f1e8 | ||
|
|
c8a26d9c5e | ||
|
|
d89b842586 | ||
|
|
6dd3ceea7e | ||
|
|
5fe68030b0 | ||
|
|
5215105440 | ||
|
|
fb69eb751a | ||
|
|
b903d51592 | ||
|
|
d1f6774e58 | ||
|
|
9e55df4141 | ||
|
|
b169e9413f | ||
|
|
c511766e9d | ||
|
|
e6f9ceb7fa | ||
|
|
eab8b48c9c | ||
|
|
46abb1009a | ||
|
|
2a42d86db0 | ||
|
|
68b0d9654c | ||
|
|
8f96cf13ac | ||
|
|
dbd179e507 | ||
|
|
9541e85600 | ||
|
|
9ecd829bfa | ||
|
|
fd4cff73ae | ||
|
|
1d6c4751de | ||
|
|
e066c1d6a1 | ||
|
|
85f5404b3b | ||
|
|
33c5893d32 | ||
|
|
b597cb0686 | ||
|
|
c5e288ca9c | ||
|
|
38d984f225 | ||
|
|
628c642040 | ||
|
|
d217cc4dd0 | ||
|
|
4f0e54b390 | ||
|
|
d7f89be31c | ||
|
|
53bcee7032 | ||
|
|
96991ae5ef | ||
|
|
068bcde451 | ||
|
|
0dda4f5646 | ||
|
|
6c57ce9dd2 | ||
|
|
6ac67a7e41 | ||
|
|
a23a585ad8 | ||
|
|
eba9562e81 | ||
|
|
ce49b409ac | ||
|
|
7222f68d4d | ||
|
|
f3f0d62f12 | ||
|
|
6e7c86e159 | ||
|
|
f5565f6700 | ||
|
|
e8d0bb0c54 | ||
|
|
33d70ccc96 | ||
|
|
53313a26af | ||
|
|
7ba180752a | ||
|
|
19736e66ad | ||
|
|
a00f8e4b76 | ||
|
|
7d9895cf5b | ||
|
|
d5f804bbb3 | ||
|
|
33a385cfa8 | ||
|
|
109d924591 | ||
|
|
a825eb3d4c | ||
|
|
3fb40677a4 | ||
|
|
833971cd28 | ||
|
|
d14b14bce9 | ||
|
|
53e26821ad | ||
|
|
1a7c06eb81 | ||
|
|
6cdbe45313 | ||
|
|
42a5b4892d | ||
|
|
9f4508b0c7 | ||
|
|
b2123ff01a | ||
|
|
43ead841a4 | ||
|
|
33b4794e83 | ||
|
|
fb91e6b1dd | ||
|
|
4a4dbf123e | ||
|
|
0decedd6a1 | ||
|
|
d2e3a63418 | ||
|
|
5eaaf9f01d | ||
|
|
d6697948c2 | ||
|
|
80d10cf4b5 | ||
|
|
ba1fb16f2d | ||
|
|
5f574667d3 | ||
|
|
c470bb1db1 | ||
|
|
26ed8ca33f | ||
|
|
3c46e16494 |
@@ -37,7 +37,7 @@ jobs:
|
|||||||
file: Dockerfile-server-base
|
file: Dockerfile-server-base
|
||||||
push: true
|
push: true
|
||||||
tags: ghcr.io/${{ github.repository }}:server-base
|
tags: ghcr.io/${{ github.repository }}:server-base
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64,linux/arm64
|
||||||
cache-from: type=gha,scope=server-base
|
cache-from: type=gha,scope=server-base
|
||||||
cache-to: type=gha,mode=max,scope=server-base
|
cache-to: type=gha,mode=max,scope=server-base
|
||||||
build-args: |
|
build-args: |
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
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) }}
|
${{ 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
|
platforms: linux/amd64,linux/arm64
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
build-args: |
|
build-args: |
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
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) }}
|
${{ 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
|
platforms: linux/amd64,linux/arm64
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
build-args: |
|
build-args: |
|
||||||
|
|||||||
+7
-7
@@ -1,5 +1,5 @@
|
|||||||
# 第一阶段:构建Vue前端
|
# 第一阶段:构建Vue前端
|
||||||
FROM node:18 as web-builder
|
FROM node:18 AS web-builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY main/manager-web/package*.json ./
|
COPY main/manager-web/package*.json ./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
@@ -7,7 +7,7 @@ COPY main/manager-web .
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# 第二阶段:构建Java后端
|
# 第二阶段:构建Java后端
|
||||||
FROM maven:3.9.4-eclipse-temurin-21 as api-builder
|
FROM maven:3.9.4-eclipse-temurin-21 AS api-builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY main/manager-api/pom.xml .
|
COPY main/manager-api/pom.xml .
|
||||||
COPY main/manager-api/src ./src
|
COPY main/manager-api/src ./src
|
||||||
@@ -18,18 +18,18 @@ FROM bellsoft/liberica-runtime-container:jre-21-glibc
|
|||||||
|
|
||||||
# 安装Nginx和字体库
|
# 安装Nginx和字体库
|
||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ \
|
apk add --no-cache --no-scripts \
|
||||||
nginx \
|
nginx \
|
||||||
bash \
|
bash \
|
||||||
fontconfig \
|
fontconfig \
|
||||||
ttf-dejavu \
|
ttf-dejavu \
|
||||||
msttcorefonts-installer \
|
|
||||||
&& ACCEPT_EULA=Y apk add --no-cache msttcorefonts-installer \
|
|
||||||
&& fc-cache -f -v \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
# 复制项目自带的中文字体
|
||||||
|
COPY main/manager-web/public/generator/static/fonts/*.ttf /usr/share/fonts/
|
||||||
|
|
||||||
# 更新字体缓存
|
# 更新字体缓存
|
||||||
RUN (printf 'YES\n' | update-ms-fonts || true) && fc-cache -f -v
|
RUN fc-cache -f -v
|
||||||
|
|
||||||
# 配置Nginx
|
# 配置Nginx
|
||||||
COPY docs/docker/nginx.conf /etc/nginx/nginx.conf
|
COPY docs/docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|||||||
@@ -183,8 +183,8 @@ Spearheaded by Professor Siyuan Liu's Team (South China University of Technology
|
|||||||
#### 🚀 部署方式选择
|
#### 🚀 部署方式选择
|
||||||
| 部署方式 | 特点 | 适用场景 | 部署文档 | 配置要求 | 视频教程 |
|
| 部署方式 | 特点 | 适用场景 | 部署文档 | 配置要求 | 视频教程 |
|
||||||
|---------|------|---------|---------|---------|---------|
|
|---------|------|---------|---------|---------|---------|
|
||||||
| **最简化安装** | 智能对话、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 | - |
|
| **最简化安装** | 智能对话、单智能体管理 | 低配置环境,数据存储在配置文件,无需数据库 | [①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) |
|
| **全模块安装** | 智能对话、多用户管理、多智能体管理、智控台界面操作 | 完整功能体验,数据存储在数据库 |[①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)
|
常见问题及相关教程,可参考[这个链接](./docs/FAQ.md)
|
||||||
|
|
||||||
@@ -211,10 +211,10 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
|||||||
|
|
||||||
| 模块名称 | 入门全免费设置 | 流式配置 |
|
| 模块名称 | 入门全免费设置 | 流式配置 |
|
||||||
|:---:|:---:|:---:|
|
|:---:|:---:|:---:|
|
||||||
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
|
| ASR(语音识别) | FunASR(本地) | 👍XunfeiStreamASR(讯飞流式) |
|
||||||
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
|
| LLM(大模型) | glm-4-flash(智谱) | 👍qwen-flash(阿里百炼) |
|
||||||
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
|
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen2.5-vl-3b-instructh(阿里百炼) |
|
||||||
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
|
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山流式) |
|
||||||
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
|
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
|
||||||
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
|
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 产品生态 👬
|
## 产品生态 👬
|
||||||
小智是一个生态,当你使用这个产品时,也可以看看其他在这个生态圈的[优秀项目](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE)
|
小智是一个生态,当你使用这个产品时,也可以看看其他在这个生态圈的[优秀项目](https://github.com/78/xiaozhi-esp32/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -181,8 +181,8 @@ Dieses Projekt bietet zwei Bereitstellungsmethoden. Bitte wählen Sie basierend
|
|||||||
#### 🚀 Auswahl der Bereitstellungsmethode
|
#### 🚀 Auswahl der Bereitstellungsmethode
|
||||||
| Bereitstellungsmethode | Funktionen | Anwendungsszenarien | Deployment-Dokumente | Konfigurationsanforderungen | Video-Tutorials |
|
| Bereitstellungsmethode | Funktionen | Anwendungsszenarien | Deployment-Dokumente | Konfigurationsanforderungen | Video-Tutorials |
|
||||||
|---------|------|---------|---------|---------|---------|
|
|---------|------|---------|---------|---------|---------|
|
||||||
| **Vereinfachte Installation** | Intelligenter Dialog, IOT, MCP, visuelle Wahrnehmung | Umgebungen mit geringer Konfiguration, Daten in Konfigurationsdateien gespeichert, keine Datenbank erforderlich | [①Docker-Version](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②Quellcode-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 Kerne 4GB bei Verwendung von `FunASR`, 2 Kerne 2GB bei allen APIs | - |
|
| **Vereinfachte Installation** | Intelligenter Dialog, Einzel-Agenten-Verwaltung | Umgebungen mit geringer Konfiguration, Daten in Konfigurationsdateien gespeichert, keine Datenbank erforderlich | [①Docker-Version](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②Quellcode-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 Kerne 4GB bei Verwendung von `FunASR`, 2 Kerne 2GB bei allen APIs | - |
|
||||||
| **Vollständige Modulinstallation** | Intelligenter Dialog, IOT, MCP-Endpunkte, Stimmabdruckerkennung, visuelle Wahrnehmung, OTA, intelligente Steuerkonsole | Vollständige Funktionserfahrung, Daten in Datenbank gespeichert |[①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) / [②Quellcode-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) / [③Quellcode-Deployment Auto-Update-Tutorial](./docs/dev-ops-integration.md) | 4 Kerne 8GB bei Verwendung von `FunASR`, 2 Kerne 4GB bei allen APIs| [Video-Tutorial für lokalen Quellcode-Start](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
|
| **Vollständige Modulinstallation** | Intelligenter Dialog, Mehrbenutzerverwaltung, Mehr-Agenten-Verwaltung, Intelligente Steuerkonsole-Bedienung | Vollständige Funktionserfahrung, Daten in Datenbank gespeichert |[①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) / [②Quellcode-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) / [③Quellcode-Deployment Auto-Update-Tutorial](./docs/dev-ops-integration.md) | 4 Kerne 8GB bei Verwendung von `FunASR`, 2 Kerne 4GB bei allen APIs| [Video-Tutorial für lokalen Quellcode-Start](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
|
||||||
|
|
||||||
Häufige Fragen und entsprechende Tutorials finden Sie unter [diesem Link](./docs/FAQ.md)
|
Häufige Fragen und entsprechende Tutorials finden Sie unter [diesem Link](./docs/FAQ.md)
|
||||||
|
|
||||||
@@ -209,10 +209,10 @@ Websocket-Schnittstellenadresse: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
|||||||
|
|
||||||
| Modulname | Einstiegslevel Kostenlose Einstellungen | Streaming-Konfiguration |
|
| Modulname | Einstiegslevel Kostenlose Einstellungen | Streaming-Konfiguration |
|
||||||
|:---:|:---:|:---:|
|
|:---:|:---:|:---:|
|
||||||
| ASR (Spracherkennung) | FunASR (Lokal) | 👍FunASR (Lokaler GPU-Modus) |
|
| ASR (Spracherkennung) | FunASR (Lokal) | 👍XunfeiStreamASR (Xunfei-Streaming) |
|
||||||
| LLM (Großes Modell) | ChatGLMLLM (Zhipu glm-4-flash) | 👍AliLLM (qwen3-235b-a22b-instruct-2507) oder 👍DoubaoLLM (doubao-1-5-pro-32k-250115) |
|
| LLM (Großes Modell) | glm-4-flash (Zhipu) | 👍qwen-flash (Alibaba Bailian) |
|
||||||
| VLLM (Vision Large Model) | ChatGLMVLLM (Zhipu glm-4v-flash) | 👍QwenVLVLLM (Qwen qwen2.5-vl-3b-instructh) |
|
| VLLM (Vision Large Model) | glm-4v-flash (Zhipu) | 👍qwen2.5-vl-3b-instructh (Alibaba Bailian) |
|
||||||
| TTS (Sprachsynthese) | ✅LinkeraiTTS (Lingxi-Streaming) | 👍HuoshanDoubleStreamTTS (Volcano Dual-Stream-Sprachsynthese) oder 👍AliyunStreamTTS (Alibaba Cloud Streaming-Sprachsynthese) |
|
| TTS (Sprachsynthese) | ✅LinkeraiTTS (Lingxi-Streaming) | 👍HuoshanDoubleStreamTTS (Volcano-Streaming) |
|
||||||
| Intent (Absichtserkennung) | function_call (Funktionsaufruf) | function_call (Funktionsaufruf) |
|
| Intent (Absichtserkennung) | function_call (Funktionsaufruf) | function_call (Funktionsaufruf) |
|
||||||
| Memory (Gedächtnisfunktion) | mem_local_short (Lokales Kurzzeitgedächtnis) | mem_local_short (Lokales Kurzzeitgedächtnis) |
|
| Memory (Gedächtnisfunktion) | mem_local_short (Lokales Kurzzeitgedächtnis) | mem_local_short (Lokales Kurzzeitgedächtnis) |
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ Wenn Sie ein Softwareentwickler sind, finden Sie hier einen [Offenen Brief an En
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Produktökosystem 👬
|
## Produktökosystem 👬
|
||||||
Xiaozhi ist ein Ökosystem. Wenn Sie dieses Produkt verwenden, können Sie sich auch andere [hervorragende Projekte](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) in diesem Ökosystem ansehen
|
Xiaozhi ist ein Ökosystem. Wenn Sie dieses Produkt verwenden, können Sie sich auch andere [hervorragende Projekte](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#related-open-source-projects) in diesem Ökosystem ansehen
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -181,8 +181,8 @@ This project provides two deployment methods. Please choose based on your specif
|
|||||||
#### 🚀 Deployment Method Selection
|
#### 🚀 Deployment Method Selection
|
||||||
| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
|
| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
|
||||||
|---------|------|---------|---------|---------|---------|
|
|---------|------|---------|---------|---------|---------|
|
||||||
| **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 | - |
|
| **Simplified Installation** | Intelligent dialogue, single agent management | 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) |
|
| **Full Module Installation** | Intelligent dialogue, multi-user management, multi-agent management, intelligent console interface operation | 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 is a test platform deployed with the latest code. You can burn 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.
|
||||||
@@ -208,10 +208,10 @@ Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
|||||||
|
|
||||||
| Module Name | Entry Level Free Settings | Streaming Configuration |
|
| Module Name | Entry Level Free Settings | Streaming Configuration |
|
||||||
|:---:|:---:|:---:|
|
|:---:|:---:|:---:|
|
||||||
| ASR(Speech Recognition) | FunASR(Local) | 👍FunASRServer or 👍DoubaoStreamASR |
|
| ASR(Speech Recognition) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
|
||||||
| LLM(Large Model) | ChatGLMLLM(Zhipu glm-4-flash) | 👍DoubaoLLM(Volcano doubao-1-5-pro-32k-250115) |
|
| LLM(Large Model) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
|
||||||
| VLLM(Vision Large Model) | ChatGLMVLLM(Zhipu glm-4v-flash) | 👍QwenVLVLLM(Qwen qwen2.5-vl-3b-instructh) |
|
| VLLM(Vision Large Model) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
|
||||||
| TTS(Speech Synthesis) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano dual-stream speech synthesis) |
|
| TTS(Speech Synthesis) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
|
||||||
| Intent(Intent Recognition) | function_call(Function calling) | function_call(Function calling) |
|
| 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) |
|
| Memory(Memory function) | mem_local_short(Local short-term memory) | mem_local_short(Local short-term memory) |
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ If you are a software developer, here is an [Open Letter to Developers](docs/con
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Product Ecosystem 👬
|
## Product Ecosystem 👬
|
||||||
Xiaozhi is an ecosystem. When using this product, you can also check out other [excellent projects](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) in this ecosystem
|
Xiaozhi is an ecosystem. When using this product, you can also check out other [excellent projects](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#related-open-source-projects) in this ecosystem
|
||||||
|
|
||||||
| Project Name | Project Address | Project Description |
|
| Project Name | Project Address | Project Description |
|
||||||
|:---------------------|:--------|:--------|
|
|:---------------------|:--------|:--------|
|
||||||
|
|||||||
+7
-7
@@ -182,8 +182,8 @@ Dự án này cung cấp hai phương pháp triển khai, vui lòng chọn theo
|
|||||||
#### 🚀 Lựa chọn phương pháp triển khai
|
#### 🚀 Lựa chọn phương pháp triển khai
|
||||||
| Phương pháp triển khai | Đặc điểm | Tình huống áp dụng | Tài liệu triển khai | Yêu cầu cấu hình | Video hướng dẫn |
|
| Phương pháp triển khai | Đặc điểm | Tình huống áp dụng | Tài liệu triển khai | Yêu cầu cấu hình | Video hướng dẫn |
|
||||||
|---------|------|---------|---------|---------|---------|
|
|---------|------|---------|---------|---------|---------|
|
||||||
| **Cài đặt tối giản** | Đối thoại thông minh, IOT, MCP, cảm nhận thị giác | Môi trường cấu hình thấp, dữ liệu lưu trong tệp cấu hình, không cần cơ sở dữ liệu | [①Phiên bản Docker](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②Triển khai mã nguồn](./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 nhân 4GB nếu dùng `FunASR`, 2 nhân 2GB nếu toàn API | - |
|
| **Cài đặt tối giản** | Đối thoại thông minh, quản lý đơn tác nhân | Môi trường cấu hình thấp, dữ liệu lưu trong tệp cấu hình, không cần cơ sở dữ liệu | [①Phiên bản Docker](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②Triển khai mã nguồn](./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 nhân 4GB nếu dùng `FunASR`, 2 nhân 2GB nếu toàn API | - |
|
||||||
| **Cài đặt toàn bộ module** | Đối thoại thông minh, IOT, điểm truy cập MCP, nhận dạng giọng nói, cảm nhận thị giác, OTA, bảng điều khiển thông minh | Trải nghiệm đầy đủ tính năng, dữ liệu lưu trong cơ sở dữ liệu |[①Phiên bản 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) / [②Triển khai mã nguồn](./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) / [③Hướng dẫn tự động cập nhật triển khai mã nguồn](./docs/dev-ops-integration.md) | 4 nhân 8GB nếu dùng `FunASR`, 2 nhân 4GB nếu toàn API| [Video hướng dẫn khởi động mã nguồn cục bộ](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
|
| **Cài đặt toàn bộ module** | Đối thoại thông minh, quản lý đa người dùng, quản lý đa tác nhân, bảng điều khiển thông minh | Trải nghiệm đầy đủ tính năng, dữ liệu lưu trong cơ sở dữ liệu |[①Phiên bản 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) / [②Triển khai mã nguồn](./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) / [③Hướng dẫn tự động cập nhật triển khai mã nguồn](./docs/dev-ops-integration.md) | 4 nhân 8GB nếu dùng `FunASR`, 2 nhân 4GB nếu toàn API| [Video hướng dẫn khởi động mã nguồn cục bộ](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
|
||||||
|
|
||||||
Câu hỏi thường gặp và hướng dẫn liên quan, vui lòng tham khảo [liên kết này](./docs/FAQ.md)
|
Câu hỏi thường gặp và hướng dẫn liên quan, vui lòng tham khảo [liên kết này](./docs/FAQ.md)
|
||||||
|
|
||||||
@@ -210,10 +210,10 @@ Công cụ kiểm tra dịch vụ: https://2662r3426b.vicp.fun/test/
|
|||||||
|
|
||||||
| Tên module | Cài đặt miễn phí cho người mới | Cấu hình streaming |
|
| Tên module | Cài đặt miễn phí cho người mới | Cấu hình streaming |
|
||||||
|:---:|:---:|:---:|
|
|:---:|:---:|:---:|
|
||||||
| ASR(Nhận dạng giọng nói) | FunASR(Local) | 👍FunASR(Chế độ GPU cục bộ) |
|
| ASR(Nhận dạng giọng nói) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
|
||||||
| LLM(Mô hình lớn) | ChatGLMLLM(Zhipu glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) hoặc 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
|
| LLM(Mô hình lớn) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
|
||||||
| VLLM(Mô hình lớn thị giác) | ChatGLMVLLM(Zhipu glm-4v-flash) | 👍QwenVLVLLM(Qwen qwen2.5-vl-3b-instructh) |
|
| VLLM(Mô hình lớn thị giác) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
|
||||||
| TTS(Tổng hợp giọng nói) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Tổng hợp giọng nói streaming kép Volcano) hoặc 👍AliyunStreamTTS(Tổng hợp giọng nói streaming Alibaba Cloud) |
|
| TTS(Tổng hợp giọng nói) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
|
||||||
| Intent(Nhận dạng ý định) | function_call(Gọi hàm) | function_call(Gọi hàm) |
|
| Intent(Nhận dạng ý định) | function_call(Gọi hàm) | function_call(Gọi hàm) |
|
||||||
| Memory(Chức năng bộ nhớ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) |
|
| Memory(Chức năng bộ nhớ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) |
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ Nếu bạn là một nhà phát triển phần mềm, đây có một [Lá thư
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Hệ sinh thái sản phẩm 👬
|
## Hệ sinh thái sản phẩm 👬
|
||||||
Xiaozhi là một hệ sinh thái, khi bạn sử dụng sản phẩm này, bạn cũng có thể xem các [dự án xuất sắc](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) khác trong hệ sinh thái này
|
Xiaozhi là một hệ sinh thái, khi bạn sử dụng sản phẩm này, bạn cũng có thể xem các [dự án xuất sắc](https://github.com/78/xiaozhi-esp32?tab=readme-ov-file#related-open-source-projects) khác trong hệ sinh thái này
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -38,10 +38,10 @@ conda install conda-forge::ffmpeg
|
|||||||
|
|
||||||
| 模块名称 | 入门全免费设置 | 流式配置 |
|
| 模块名称 | 入门全免费设置 | 流式配置 |
|
||||||
|:---:|:---:|:---:|
|
|:---:|:---:|:---:|
|
||||||
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
|
| ASR(语音识别) | FunASR(本地) | 👍XunfeiStreamASR(讯飞流式) |
|
||||||
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
|
| LLM(大模型) | glm-4-flash(智谱) | 👍qwen-flash(阿里百炼) |
|
||||||
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
|
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen2.5-vl-3b-instructh(阿里百炼) |
|
||||||
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
|
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山流式) |
|
||||||
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
|
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
|
||||||
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
|
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
|
||||||
|
|
||||||
@@ -69,6 +69,7 @@ VAD:
|
|||||||
### 9、编译固件相关教程
|
### 9、编译固件相关教程
|
||||||
1、[如何自己编译小智固件](./firmware-build.md)<br/>
|
1、[如何自己编译小智固件](./firmware-build.md)<br/>
|
||||||
2、[如何基于虾哥编译好的固件修改OTA地址](./firmware-setting.md)<br/>
|
2、[如何基于虾哥编译好的固件修改OTA地址](./firmware-setting.md)<br/>
|
||||||
|
3、[单模块部署如何配置固件OTA自动升级](./ota-upgrade-guide.md)<br/>
|
||||||
|
|
||||||
### 10、拓展相关教程
|
### 10、拓展相关教程
|
||||||
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
|
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
### 2.将音色资源ID分配给系统账号
|
### 2.将音色资源ID分配给系统账号
|
||||||
|
|
||||||
|
使用超级管理员账号登录智控台,点击顶部`参数字典`,在下拉菜单中,点击`系统功能配置`页面。在页面上勾选`音色克隆`,点击保存配置。即可在顶部菜单看到`音色克隆`按钮。
|
||||||
|
|
||||||
使用超级管理员账号登录智控台,点击顶部【音色克隆】、【音色资源】。
|
使用超级管理员账号登录智控台,点击顶部【音色克隆】、【音色资源】。
|
||||||
|
|
||||||
点击新增按钮,在【平台名称】选择“火山双流式语音合成”;
|
点击新增按钮,在【平台名称】选择“火山双流式语音合成”;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ MQTT_PORT=1883 # MQTT服务器端口
|
|||||||
UDP_PORT=8884 # UDP服务器端口
|
UDP_PORT=8884 # UDP服务器端口
|
||||||
API_PORT=8007 # 管理API端口
|
API_PORT=8007 # 管理API端口
|
||||||
MQTT_SIGNATURE_KEY=test # MQTT签名密钥
|
MQTT_SIGNATURE_KEY=test # MQTT签名密钥
|
||||||
|
SERVER_SECRET=Te1st12134 # 服务器密钥,请保持和智控台(server.secret)一致或者和xiaozhi-server里(server.auth_key)保持一致
|
||||||
```
|
```
|
||||||
请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。
|
请注意`PUBLIC_IP`配置,确保其与实际公网IP一致,如果有域名就填域名。
|
||||||
|
|
||||||
@@ -85,6 +86,13 @@ MQTT_SIGNATURE_KEY=test # MQTT签名密钥
|
|||||||
- 注意不要用简单的密码,比如`123456`、`test`等。
|
- 注意不要用简单的密码,比如`123456`、`test`等。
|
||||||
- 注意不要用简单的密码,比如`123456`、`test`等。
|
- 注意不要用简单的密码,比如`123456`、`test`等。
|
||||||
|
|
||||||
|
`SERVER_SECRET` 是用生成websocket连接的认证信息。
|
||||||
|
|
||||||
|
1、如果你是全模块部署,且你的智控台的参数管理里`server.auth.enabled`设置成了`true`,那么,`SERVER_SECRET`需要和智控台(`server.secret`)保持一致。
|
||||||
|
|
||||||
|
2、如果你是单模块部署,且你在配置文件里把`server.auth.enabled`设置成了`true`,那么,`SERVER_SECRET`需要和配置文件里(`server.auth_key`)保持一致。
|
||||||
|
|
||||||
|
|
||||||
6. 启动MQTT网关
|
6. 启动MQTT网关
|
||||||
```
|
```
|
||||||
# 启动服务
|
# 启动服务
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# 单模块部署固件OTA自动升级配置指南
|
||||||
|
|
||||||
|
本教程将指导你如何在**单模块部署**场景下配置固件OTA自动升级功能,实现设备固件的自动更新。
|
||||||
|
|
||||||
|
如果你已经使用**全模块部署**,请忽略本教程。
|
||||||
|
|
||||||
|
## 功能介绍
|
||||||
|
|
||||||
|
在单模块部署中,xiaozhi-server内置了OTA固件管理功能,可以自动检测设备版本并下发升级固件。系统会根据设备型号和当前版本,自动匹配并推送最新的固件版本。
|
||||||
|
|
||||||
|
## 前提条件
|
||||||
|
|
||||||
|
- 你已经成功进行**单模块部署**并运行xiaozhi-server
|
||||||
|
- 设备能够正常连接到服务器
|
||||||
|
|
||||||
|
## 第一步 准备固件文件
|
||||||
|
|
||||||
|
### 1. 创建固件存放目录
|
||||||
|
|
||||||
|
固件文件需要放在`data/bin/`目录下。如果该目录不存在,请手动创建:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p data/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 固件文件命名规则
|
||||||
|
|
||||||
|
固件文件必须遵循以下命名格式:
|
||||||
|
|
||||||
|
```
|
||||||
|
{设备型号}_{版本号}.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**命名规则说明:**
|
||||||
|
- `设备型号`:设备的型号名称,例如 `lichuang-dev`、`bread-compact-wifi` 等
|
||||||
|
- `版本号`:固件版本号,必须以数字开头,支持数字、字母、点号、下划线和短横线,例如 `1.6.6`、`2.0.0` 等
|
||||||
|
- 文件扩展名必须是 `.bin`
|
||||||
|
|
||||||
|
**命名示例:**
|
||||||
|
```
|
||||||
|
bread-compact-wifi_1.6.6.bin
|
||||||
|
lichuang-dev_2.0.0.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 放置固件文件
|
||||||
|
|
||||||
|
将准备好的固件文件(.bin文件)复制到`data/bin/`目录下:
|
||||||
|
|
||||||
|
重要的事情说三遍:升级的bin文件是`xiaozhi.bin`,不是全量固件文件`merged-binary.bin`!
|
||||||
|
|
||||||
|
重要的事情说三遍:升级的bin文件是`xiaozhi.bin`,不是全量固件文件`merged-binary.bin`!
|
||||||
|
|
||||||
|
重要的事情说三遍:升级的bin文件是`xiaozhi.bin`,不是全量固件文件`merged-binary.bin`!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp xiaozhi.bin data/bin/设备型号_版本号.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
例如:
|
||||||
|
```bash
|
||||||
|
cp xiaozhi.bin data/bin/bread-compact-wifi_1.6.6.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第二步 配置公网访问地址(仅公网部署需要)
|
||||||
|
|
||||||
|
**注意:此步骤仅适用于单模块公网部署的场景。**
|
||||||
|
|
||||||
|
如果你的xiaozhi-server是公网部署(使用公网IP或域名),**必须**配置`server.vision_explain`参数,因为OTA固件下载地址会使用该配置的域名和端口。
|
||||||
|
|
||||||
|
如果你是局域网部署,可以跳过此步骤。
|
||||||
|
|
||||||
|
### 为什么要配置这个参数?
|
||||||
|
|
||||||
|
在单模块部署中,系统生成固件下载地址时,会使用`vision_explain`配置的域名和端口作为基础地址。如果不配置或配置错误,设备将无法访问固件下载地址。
|
||||||
|
|
||||||
|
### 配置方法
|
||||||
|
|
||||||
|
打开`data/.config.yaml`文件,找到`server`配置段,设置`vision_explain`参数:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
vision_explain: http://你的域名或IP:端口号/mcp/vision/explain
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置示例:**
|
||||||
|
|
||||||
|
局域网部署(默认):
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
vision_explain: http://192.168.1.100:8003/mcp/vision/explain
|
||||||
|
```
|
||||||
|
|
||||||
|
公网域名部署:
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
vision_explain: http://yourdomain.com:8003/mcp/vision/explain
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
- 域名或IP必须是设备能够访问的地址
|
||||||
|
- 如果使用Docker部署,不能使用Docker内部地址(如127.0.0.1或localhost)
|
||||||
|
- 如果你使用了nginx反向代理,请填写对外的地址和端口号,不是本项目运行的端口号
|
||||||
|
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 1. 设备收不到固件更新
|
||||||
|
|
||||||
|
**可能原因和解决方法:**
|
||||||
|
|
||||||
|
- 检查固件文件命名是否符合规则:`{型号}_{版本号}.bin`
|
||||||
|
- 检查固件文件是否正确放置在`data/bin/`目录
|
||||||
|
- 检查设备型号是否与固件文件名中的型号匹配
|
||||||
|
- 检查固件版本号是否高于设备当前版本
|
||||||
|
- 查看服务器日志,确认OTA请求是否正常处理
|
||||||
|
|
||||||
|
### 2. 设备报告下载地址无法访问
|
||||||
|
|
||||||
|
**可能原因和解决方法:**
|
||||||
|
|
||||||
|
- 检查`server.vision_explain`配置的域名或IP是否正确
|
||||||
|
- 确认端口号配置正确(默认8003)
|
||||||
|
- 如果是公网部署,确保设备能够访问该公网地址
|
||||||
|
- 如果是Docker部署,确保不是使用了内部地址(127.0.0.1)
|
||||||
|
- 检查防火墙是否开放了对应端口
|
||||||
|
- 如果你使用了nginx反向代理,请填写对外的地址和端口号,不是本项目运行的端口号
|
||||||
|
|
||||||
|
### 3. 如何确认设备当前版本
|
||||||
|
|
||||||
|
查看OTA请求日志,日志中会显示设备上报的版本号:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ota_handler] - 设备 AA:BB:CC:DD:EE:FF 固件已是最新: 1.6.6
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 固件文件放置后没有生效
|
||||||
|
|
||||||
|
系统有30秒的缓存时间(默认),可以:
|
||||||
|
- 等待30秒后再让设备发起OTA请求
|
||||||
|
- 重启xiaozhi-server服务
|
||||||
|
- 调整`firmware_cache_ttl`配置为更短的时间
|
||||||
@@ -43,7 +43,7 @@ public class SwaggerConfig {
|
|||||||
public GroupedOpenApi oatApi() {
|
public GroupedOpenApi oatApi() {
|
||||||
return GroupedOpenApi.builder()
|
return GroupedOpenApi.builder()
|
||||||
.group("ota")
|
.group("ota")
|
||||||
.pathsToMatch("/ota/**")
|
.pathsToMatch("/ota/**", "/otaMag/**")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ public interface Constant {
|
|||||||
/**
|
/**
|
||||||
* 版本号
|
* 版本号
|
||||||
*/
|
*/
|
||||||
public static final String VERSION = "0.8.10";
|
public static final String VERSION = "0.9.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 无效固件URL
|
* 无效固件URL
|
||||||
|
|||||||
@@ -240,4 +240,9 @@ public interface ErrorCode {
|
|||||||
int RAG_ADAPTER_CACHE_ERROR = 10191; // 适配器缓存错误
|
int RAG_ADAPTER_CACHE_ERROR = 10191; // 适配器缓存错误
|
||||||
int RAG_ADAPTER_TYPE_NOT_FOUND = 10192; // 适配器类型未找到
|
int RAG_ADAPTER_TYPE_NOT_FOUND = 10192; // 适配器类型未找到
|
||||||
|
|
||||||
|
// 设备工具相关错误码
|
||||||
|
int DEVICE_ID_NOT_NULL = 10193; // 设备ID不能为空
|
||||||
|
int DEVICE_NOT_EXIST = 10194; // 设备不存在
|
||||||
|
int OTA_UPLOAD_COUNT_EXCEED = 10195; // OTA上传次数超过限制
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,4 +159,32 @@ public class RedisKeys {
|
|||||||
public static String getKnowledgeBaseCacheKey(String datasetId) {
|
public static String getKnowledgeBaseCacheKey(String datasetId) {
|
||||||
return "knowledge:base:" + datasetId;
|
return "knowledge:base:" + datasetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取临时注册设备标记key
|
||||||
|
*/
|
||||||
|
public static String getTmpRegisterMacKey(String deviceId) {
|
||||||
|
return "tmp_register_mac:" + deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OTA绑定设备
|
||||||
|
*/
|
||||||
|
public static String getOtaActivationCode(String activationCode) {
|
||||||
|
return "ota:activation:code:" + activationCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OTA获取设备mac相关信息
|
||||||
|
*/
|
||||||
|
public static String getOtaDeviceActivationInfo(String deviceId) {
|
||||||
|
return "ota:activation:data:" + deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OTA上传次数
|
||||||
|
*/
|
||||||
|
public static String getOtaUploadCountKey(Long username) {
|
||||||
|
return "ota:upload:count:" + username;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package xiaozhi.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用工具类
|
||||||
|
*/
|
||||||
|
public class ToolUtil {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ToolUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象是否不为空(新增)
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(Object o) {
|
||||||
|
return !isEmpty(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象是否为空
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o instanceof String) {
|
||||||
|
if (o.toString().trim().equals("")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof List) {
|
||||||
|
if (((List) o).size() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof Map) {
|
||||||
|
if (((Map) o).size() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof Set) {
|
||||||
|
if (((Set) o).size() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof Object[]) {
|
||||||
|
if (((Object[]) o).length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof int[]) {
|
||||||
|
if (((int[]) o).length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (o instanceof long[]) {
|
||||||
|
if (((long[]) o).length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象组中是否存在空对象
|
||||||
|
*/
|
||||||
|
public static boolean isOneEmpty(Object... os) {
|
||||||
|
for (Object o : os) {
|
||||||
|
if (isEmpty(o)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象组中是否全是空对象
|
||||||
|
*/
|
||||||
|
public static boolean isAllEmpty(Object... os) {
|
||||||
|
for (Object o : os) {
|
||||||
|
if (!isEmpty(o)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-2
@@ -44,6 +44,7 @@ import xiaozhi.modules.agent.entity.AgentEntity;
|
|||||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||||
import xiaozhi.modules.agent.service.AgentChatAudioService;
|
import xiaozhi.modules.agent.service.AgentChatAudioService;
|
||||||
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentChatSummaryService;
|
||||||
import xiaozhi.modules.agent.service.AgentContextProviderService;
|
import xiaozhi.modules.agent.service.AgentContextProviderService;
|
||||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
@@ -66,14 +67,19 @@ public class AgentController {
|
|||||||
private final AgentChatAudioService agentChatAudioService;
|
private final AgentChatAudioService agentChatAudioService;
|
||||||
private final AgentPluginMappingService agentPluginMappingService;
|
private final AgentPluginMappingService agentPluginMappingService;
|
||||||
private final AgentContextProviderService agentContextProviderService;
|
private final AgentContextProviderService agentContextProviderService;
|
||||||
|
private final AgentChatSummaryService agentChatSummaryService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "获取用户智能体列表")
|
@Operation(summary = "获取用户智能体列表")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<List<AgentDTO>> getUserAgents() {
|
public Result<List<AgentDTO>> getUserAgents(
|
||||||
|
@RequestParam(value = "keyword", required = false) String keyword,
|
||||||
|
@RequestParam(value = "searchType", defaultValue = "name") String searchType) {
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
List<AgentDTO> agents = agentService.getUserAgents(user.getId());
|
|
||||||
|
// 直接调用整合后的getUserAgents方法,无需再区分搜索和普通查询
|
||||||
|
List<AgentDTO> agents = agentService.getUserAgents(user.getId(), keyword, searchType);
|
||||||
return new Result<List<AgentDTO>>().ok(agents);
|
return new Result<List<AgentDTO>>().ok(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +125,27 @@ public class AgentController {
|
|||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/chat-summary/{sessionId}/save")
|
||||||
|
@Operation(summary = "根据会话ID生成聊天记录总结并保存(异步执行)")
|
||||||
|
public Result<Void> generateAndSaveChatSummary(@PathVariable String sessionId) {
|
||||||
|
try {
|
||||||
|
// 异步执行总结生成任务,立即返回成功响应
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
agentChatSummaryService.generateAndSaveChatSummary(sessionId);
|
||||||
|
System.out.println("异步执行会话 " + sessionId + " 的聊天记录总结完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("异步执行会话 " + sessionId + " 的聊天记录总结失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// 立即返回成功响应,不等待总结生成完成
|
||||||
|
return new Result<Void>().ok(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new Result<Void>().error("启动异步总结生成任务失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@Operation(summary = "更新智能体")
|
@Operation(summary = "更新智能体")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
@@ -186,6 +213,7 @@ public class AgentController {
|
|||||||
List<AgentChatHistoryDTO> result = agentChatHistoryService.getChatHistoryBySessionId(id, sessionId);
|
List<AgentChatHistoryDTO> result = agentChatHistoryService.getChatHistoryBySessionId(id, sessionId);
|
||||||
return new Result<List<AgentChatHistoryDTO>>().ok(result);
|
return new Result<List<AgentChatHistoryDTO>>().ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/chat-history/user")
|
@GetMapping("/{id}/chat-history/user")
|
||||||
@Operation(summary = "获取智能体聊天记录(用户)")
|
@Operation(summary = "获取智能体聊天记录(用户)")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
@@ -247,4 +275,6 @@ public class AgentController {
|
|||||||
.body(audioData);
|
.body(audioData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -28,7 +28,7 @@ public class AgentMcpAccessPointController {
|
|||||||
/**
|
/**
|
||||||
* 获取智能体的Mcp接入点地址
|
* 获取智能体的Mcp接入点地址
|
||||||
*
|
*
|
||||||
* @param audioId 智能体id
|
* @param agentId 智能体id
|
||||||
* @return 返回错误提醒或者Mcp接入点地址
|
* @return 返回错误提醒或者Mcp接入点地址
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "获取智能体的Mcp接入点地址")
|
@Operation(summary = "获取智能体的Mcp接入点地址")
|
||||||
|
|||||||
+18
-6
@@ -1,6 +1,9 @@
|
|||||||
package xiaozhi.modules.agent.dao;
|
package xiaozhi.modules.agent.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
@@ -15,12 +18,6 @@ import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
|
|||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface AiAgentChatHistoryDao extends BaseMapper<AgentChatHistoryEntity> {
|
public interface AiAgentChatHistoryDao extends BaseMapper<AgentChatHistoryEntity> {
|
||||||
/**
|
|
||||||
* 根据智能体ID删除音频
|
|
||||||
*
|
|
||||||
* @param agentId 智能体ID
|
|
||||||
*/
|
|
||||||
void deleteAudioByAgentId(String agentId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据智能体ID删除聊天历史记录
|
* 根据智能体ID删除聊天历史记录
|
||||||
@@ -35,4 +32,19 @@ public interface AiAgentChatHistoryDao extends BaseMapper<AgentChatHistoryEntity
|
|||||||
* @param agentId 智能体ID
|
* @param agentId 智能体ID
|
||||||
*/
|
*/
|
||||||
void deleteAudioIdByAgentId(String agentId);
|
void deleteAudioIdByAgentId(String agentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据智能体ID获取所有音频ID列表
|
||||||
|
*
|
||||||
|
* @param agentId 智能体ID
|
||||||
|
* @return 音频ID列表
|
||||||
|
*/
|
||||||
|
List<String> getAudioIdsByAgentId(String agentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除音频
|
||||||
|
*
|
||||||
|
* @param audioIds 音频ID列表
|
||||||
|
*/
|
||||||
|
void deleteAudioByIds(@Param("audioIds") List<String> audioIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package xiaozhi.modules.agent.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体聊天记录总结DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "智能体聊天记录总结对象")
|
||||||
|
public class AgentChatSummaryDTO {
|
||||||
|
|
||||||
|
@Schema(description = "会话ID")
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
@Schema(description = "智能体ID")
|
||||||
|
private String agentId;
|
||||||
|
|
||||||
|
@Schema(description = "总结内容")
|
||||||
|
private String summary;
|
||||||
|
|
||||||
|
@Schema(description = "总结状态")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "错误信息")
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
public AgentChatSummaryDTO() {
|
||||||
|
this.success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentChatSummaryDTO(String sessionId, String agentId, String summary) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.agentId = agentId;
|
||||||
|
this.summary = summary;
|
||||||
|
this.success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentChatSummaryDTO(String sessionId, String errorMessage) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
this.success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package xiaozhi.modules.agent.dto;
|
package xiaozhi.modules.agent.dto;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package xiaozhi.modules.agent.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体聊天记录总结服务接口
|
||||||
|
*/
|
||||||
|
public interface AgentChatSummaryService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据会话ID生成聊天记录总结并保存到智能体记忆
|
||||||
|
*
|
||||||
|
* @param sessionId 会话ID
|
||||||
|
* @return 保存结果
|
||||||
|
*/
|
||||||
|
boolean generateAndSaveChatSummary(String sessionId);
|
||||||
|
}
|
||||||
@@ -54,9 +54,11 @@ public interface AgentService extends BaseService<AgentEntity> {
|
|||||||
* 获取用户智能体列表
|
* 获取用户智能体列表
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
|
* @param keyword 搜索关键词
|
||||||
|
* @param searchType 搜索类型(name - 按名称搜索,mac - 按MAC地址搜索)
|
||||||
* @return 智能体列表
|
* @return 智能体列表
|
||||||
*/
|
*/
|
||||||
List<AgentDTO> getUserAgents(Long userId);
|
List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据智能体ID获取设备数量
|
* 根据智能体ID获取设备数量
|
||||||
@@ -98,4 +100,6 @@ public interface AgentService extends BaseService<AgentEntity> {
|
|||||||
* @return 创建的智能体ID
|
* @return 创建的智能体ID
|
||||||
*/
|
*/
|
||||||
String createAgent(AgentCreateDTO dto);
|
String createAgent(AgentCreateDTO dto);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -17,6 +17,7 @@ import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
|
|||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||||
import xiaozhi.modules.agent.service.AgentChatAudioService;
|
import xiaozhi.modules.agent.service.AgentChatAudioService;
|
||||||
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentChatSummaryService;
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
|
import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
@@ -36,6 +37,7 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
|||||||
private final AgentService agentService;
|
private final AgentService agentService;
|
||||||
private final AgentChatHistoryService agentChatHistoryService;
|
private final AgentChatHistoryService agentChatHistoryService;
|
||||||
private final AgentChatAudioService agentChatAudioService;
|
private final AgentChatAudioService agentChatAudioService;
|
||||||
|
private final AgentChatSummaryService agentChatSummaryService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final DeviceService deviceService;
|
private final DeviceService deviceService;
|
||||||
|
|
||||||
@@ -50,7 +52,8 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
|||||||
public Boolean report(AgentChatHistoryReportDTO report) {
|
public Boolean report(AgentChatHistoryReportDTO report) {
|
||||||
String macAddress = report.getMacAddress();
|
String macAddress = report.getMacAddress();
|
||||||
Byte chatType = report.getChatType();
|
Byte chatType = report.getChatType();
|
||||||
Long reportTimeMillis = null != report.getReportTime() ? report.getReportTime() * 1000 : System.currentTimeMillis();
|
Long reportTimeMillis = null != report.getReportTime() ? report.getReportTime() * 1000
|
||||||
|
: System.currentTimeMillis();
|
||||||
log.info("小智设备聊天上报请求: macAddress={}, type={} reportTime={}", macAddress, chatType, reportTimeMillis);
|
log.info("小智设备聊天上报请求: macAddress={}, type={} reportTime={}", macAddress, chatType, reportTimeMillis);
|
||||||
|
|
||||||
// 根据设备MAC地址查询对应的默认智能体,判断是否需要上报
|
// 根据设备MAC地址查询对应的默认智能体,判断是否需要上报
|
||||||
@@ -105,7 +108,8 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
|||||||
/**
|
/**
|
||||||
* 组装上报数据
|
* 组装上报数据
|
||||||
*/
|
*/
|
||||||
private void saveChatText(AgentChatHistoryReportDTO report, String agentId, String macAddress, String audioId, Long reportTime) {
|
private void saveChatText(AgentChatHistoryReportDTO report, String agentId, String macAddress, String audioId,
|
||||||
|
Long reportTime) {
|
||||||
// 构建聊天记录实体
|
// 构建聊天记录实体
|
||||||
AgentChatHistoryEntity entity = AgentChatHistoryEntity.builder()
|
AgentChatHistoryEntity entity = AgentChatHistoryEntity.builder()
|
||||||
.macAddress(macAddress)
|
.macAddress(macAddress)
|
||||||
|
|||||||
+12
-2
@@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.ListUtil;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ import xiaozhi.common.constant.Constant;
|
|||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
|
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
|
||||||
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
|
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
|
||||||
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
||||||
@@ -84,7 +86,15 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText) {
|
public void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText) {
|
||||||
if (deleteAudio) {
|
if (deleteAudio) {
|
||||||
baseMapper.deleteAudioByAgentId(agentId);
|
// 分批删除音频,避免超时
|
||||||
|
List<String> audioIds = baseMapper.getAudioIdsByAgentId(agentId);
|
||||||
|
if (ToolUtil.isNotEmpty(audioIds)) {
|
||||||
|
// 每批删除1000条
|
||||||
|
List<List<String>> batch = ListUtil.split(audioIds, 1000);
|
||||||
|
batch.forEach(dataList->{
|
||||||
|
baseMapper.deleteAudioByIds(dataList);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (deleteAudio && !deleteText) {
|
if (deleteAudio && !deleteText) {
|
||||||
baseMapper.deleteAudioIdByAgentId(agentId);
|
baseMapper.deleteAudioIdByAgentId(agentId);
|
||||||
@@ -107,7 +117,7 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
|
|||||||
// 添加此行,确保查询结果按照创建时间降序排列
|
// 添加此行,确保查询结果按照创建时间降序排列
|
||||||
// 使用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
|
// 使用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
|
||||||
// id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
|
// id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
|
||||||
.orderByDesc(AgentChatHistoryEntity::getId);
|
.orderByDesc(AgentChatHistoryEntity::getId);
|
||||||
|
|
||||||
// 构建分页查询,查询前50页数据
|
// 构建分页查询,查询前50页数据
|
||||||
Page<AgentChatHistoryEntity> pageParam = new Page<>(0, 50);
|
Page<AgentChatHistoryEntity> pageParam = new Page<>(0, 50);
|
||||||
|
|||||||
+423
@@ -0,0 +1,423 @@
|
|||||||
|
package xiaozhi.modules.agent.service.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
||||||
|
import xiaozhi.modules.agent.dto.AgentChatSummaryDTO;
|
||||||
|
import xiaozhi.modules.agent.dto.AgentMemoryDTO;
|
||||||
|
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
|
||||||
|
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
|
||||||
|
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentChatSummaryService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
|
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||||
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.modules.llm.service.LLMService;
|
||||||
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能体聊天记录总结服务实现类
|
||||||
|
* 实现Python端mem_local_short.py中的总结逻辑
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AgentChatSummaryServiceImpl.class);
|
||||||
|
|
||||||
|
private final AgentChatHistoryService agentChatHistoryService;
|
||||||
|
private final AgentService agentService;
|
||||||
|
private final DeviceService deviceService;
|
||||||
|
private final LLMService llmService;
|
||||||
|
private final ModelConfigService modelConfigService;
|
||||||
|
|
||||||
|
// 总结规则常量
|
||||||
|
private static final int MAX_SUMMARY_LENGTH = 1800; // 最大总结长度
|
||||||
|
private static final Pattern JSON_PATTERN = Pattern.compile("\\{.*?\\}", Pattern.DOTALL);
|
||||||
|
private static final Pattern DEVICE_CONTROL_PATTERN = Pattern.compile("设备控制|设备操作|控制设备|设备状态",
|
||||||
|
Pattern.CASE_INSENSITIVE);
|
||||||
|
private static final Pattern WEATHER_PATTERN = Pattern.compile("天气|温度|湿度|降雨|气象", Pattern.CASE_INSENSITIVE);
|
||||||
|
private static final Pattern DATE_PATTERN = Pattern.compile("日期|时间|星期|月份|年份", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
private AgentChatSummaryDTO generateChatSummary(String sessionId) {
|
||||||
|
try {
|
||||||
|
System.out.println("开始生成会话 " + sessionId + " 的聊天记录总结");
|
||||||
|
|
||||||
|
// 1. 根据sessionId获取聊天记录
|
||||||
|
List<AgentChatHistoryDTO> chatHistory = getChatHistoryBySessionId(sessionId);
|
||||||
|
if (chatHistory == null || chatHistory.isEmpty()) {
|
||||||
|
return new AgentChatSummaryDTO(sessionId, "未找到该会话的聊天记录");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取智能体信息
|
||||||
|
String agentId = getAgentIdFromSession(sessionId, chatHistory);
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
return new AgentChatSummaryDTO(sessionId, "无法获取智能体信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 提取关键对话内容
|
||||||
|
List<String> meaningfulMessages = extractMeaningfulMessages(chatHistory);
|
||||||
|
if (meaningfulMessages.isEmpty()) {
|
||||||
|
return new AgentChatSummaryDTO(sessionId, "没有有效的对话内容可总结");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 生成总结(generateSummaryFromMessages方法已包含长度限制逻辑)
|
||||||
|
String summary = generateSummaryFromMessages(meaningfulMessages, agentId);
|
||||||
|
|
||||||
|
log.info("成功生成会话 {} 的聊天记录总结,长度: {} 字符", sessionId, summary.length());
|
||||||
|
return new AgentChatSummaryDTO(sessionId, agentId, summary);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成会话 {} 的聊天记录总结时发生错误: {}", sessionId, e.getMessage());
|
||||||
|
return new AgentChatSummaryDTO(sessionId, "生成总结时发生错误: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean generateAndSaveChatSummary(String sessionId) {
|
||||||
|
try {
|
||||||
|
// 1. 生成总结
|
||||||
|
AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId);
|
||||||
|
if (!summaryDTO.isSuccess()) {
|
||||||
|
log.info("生成总结失败: {}", summaryDTO.getErrorMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取设备信息(通过会话关联的设备)
|
||||||
|
DeviceEntity device = getDeviceBySessionId(sessionId);
|
||||||
|
if (device == null) {
|
||||||
|
log.info("未找到与会话 {} 关联的设备", sessionId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新智能体记忆
|
||||||
|
AgentMemoryDTO memoryDTO = new AgentMemoryDTO();
|
||||||
|
memoryDTO.setSummaryMemory(summaryDTO.getSummary());
|
||||||
|
|
||||||
|
// 调用现有接口更新记忆
|
||||||
|
agentService.updateAgentById(device.getAgentId(),
|
||||||
|
new AgentUpdateDTO() {
|
||||||
|
{
|
||||||
|
setSummaryMemory(summaryDTO.getSummary());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, device.getAgentId());
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("保存会话 {} 的聊天记录总结时发生错误: {}", sessionId, e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据会话ID获取聊天记录
|
||||||
|
*/
|
||||||
|
private List<AgentChatHistoryDTO> getChatHistoryBySessionId(String sessionId) {
|
||||||
|
try {
|
||||||
|
// 这里需要根据sessionId获取聊天记录
|
||||||
|
// 由于现有接口需要agentId,我们需要先找到关联的agentId
|
||||||
|
String agentId = findAgentIdBySessionId(sessionId);
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return agentChatHistoryService.getChatHistoryBySessionId(agentId, sessionId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取会话 {} 的聊天记录失败: {}", sessionId, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据会话ID查找关联的智能体ID
|
||||||
|
*/
|
||||||
|
private String findAgentIdBySessionId(String sessionId) {
|
||||||
|
try {
|
||||||
|
// 查询该会话的第一条记录获取agentId
|
||||||
|
QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.select("agent_id")
|
||||||
|
.eq("session_id", sessionId)
|
||||||
|
.last("LIMIT 1");
|
||||||
|
|
||||||
|
AgentChatHistoryEntity entity = agentChatHistoryService.getOne(wrapper);
|
||||||
|
return entity != null ? entity.getAgentId() : null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("根据会话ID {} 查找智能体ID失败: {}", sessionId, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从会话中获取智能体ID
|
||||||
|
*/
|
||||||
|
private String getAgentIdFromSession(String sessionId, List<AgentChatHistoryDTO> chatHistory) {
|
||||||
|
// 直接从数据库查询智能体ID
|
||||||
|
return findAgentIdBySessionId(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取有意义的对话内容(只提取用户消息,排除AI回复)
|
||||||
|
*/
|
||||||
|
private List<String> extractMeaningfulMessages(List<AgentChatHistoryDTO> chatHistory) {
|
||||||
|
List<String> meaningfulMessages = new ArrayList<>();
|
||||||
|
|
||||||
|
for (AgentChatHistoryDTO message : chatHistory) {
|
||||||
|
// 只处理用户消息(chatType = 1)
|
||||||
|
if (message.getChatType() != null && message.getChatType() == 1) {
|
||||||
|
String content = extractContentFromMessage(message);
|
||||||
|
if (isMeaningfulMessage(content)) {
|
||||||
|
meaningfulMessages.add(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return meaningfulMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从消息中提取内容(处理JSON格式)
|
||||||
|
*/
|
||||||
|
private String extractContentFromMessage(AgentChatHistoryDTO message) {
|
||||||
|
String content = message.getContent();
|
||||||
|
if (StringUtils.isBlank(content)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理JSON格式内容(与前端ChatHistoryDialog.vue逻辑一致)
|
||||||
|
Matcher matcher = JSON_PATTERN.matcher(content);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String jsonContent = matcher.group();
|
||||||
|
// 简化处理:提取JSON中的文本内容
|
||||||
|
return extractTextFromJson(jsonContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从JSON中提取文本内容
|
||||||
|
*/
|
||||||
|
private String extractTextFromJson(String jsonContent) {
|
||||||
|
// 简化处理:提取"content"字段的值
|
||||||
|
Pattern contentPattern = Pattern.compile("\"content\"\s*:\s*\"([^\"]*)\"");
|
||||||
|
Matcher matcher = contentPattern.matcher(jsonContent);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
return jsonContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为有意义的消息
|
||||||
|
*/
|
||||||
|
private boolean isMeaningfulMessage(String content) {
|
||||||
|
if (StringUtils.isBlank(content)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除设备控制信息
|
||||||
|
if (DEVICE_CONTROL_PATTERN.matcher(content).find()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除日期天气等无关内容
|
||||||
|
if (WEATHER_PATTERN.matcher(content).find() || DATE_PATTERN.matcher(content).find()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除过短的消息
|
||||||
|
return content.length() >= 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从消息生成总结
|
||||||
|
*/
|
||||||
|
private String generateSummaryFromMessages(List<String> messages, String agentId) {
|
||||||
|
if (messages.isEmpty()) {
|
||||||
|
return "本次对话内容较少,没有需要总结的重要信息。";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建完整的对话内容
|
||||||
|
StringBuilder conversation = new StringBuilder();
|
||||||
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
|
conversation.append("消息").append(i + 1).append(": ").append(messages.get(i)).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取当前智能体的历史记忆
|
||||||
|
String historyMemory = getCurrentAgentMemory(agentId);
|
||||||
|
|
||||||
|
// 调用LLM服务进行智能总结,传递agentId以获取正确的模型配置
|
||||||
|
String summary = callJavaLLMForSummaryWithHistory(conversation.toString(), historyMemory, agentId);
|
||||||
|
|
||||||
|
// 应用总结规则:限制最大长度
|
||||||
|
if (summary.length() > MAX_SUMMARY_LENGTH) {
|
||||||
|
summary = summary.substring(0, MAX_SUMMARY_LENGTH) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用Java端LLM服务失败: {}", e.getMessage());
|
||||||
|
throw new RuntimeException("LLM服务不可用,无法生成聊天总结");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前智能体的历史记忆
|
||||||
|
*/
|
||||||
|
private String getCurrentAgentMemory(String agentId) {
|
||||||
|
try {
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取智能体信息
|
||||||
|
AgentInfoVO agentInfo = agentService.getAgentById(agentId);
|
||||||
|
if (agentInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回智能体的当前总结记忆
|
||||||
|
return agentInfo.getSummaryMemory();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取智能体历史记忆失败,agentId: {}, 错误: {}", agentId, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用Java端LLM服务进行智能总结(支持历史记忆合并)
|
||||||
|
*/
|
||||||
|
private String callJavaLLMForSummaryWithHistory(String conversation, String historyMemory, String agentId) {
|
||||||
|
try {
|
||||||
|
// 获取智能体配置,从中提取记忆总结的模型ID
|
||||||
|
String modelId = getMemorySummaryModelId(agentId);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(modelId)) {
|
||||||
|
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
|
||||||
|
return llmService.generateSummaryWithHistory(conversation, historyMemory, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用指定的模型ID调用LLM服务(支持历史记忆合并)
|
||||||
|
String summary = llmService.generateSummaryWithHistory(conversation, historyMemory, null, modelId);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Java端LLM服务返回异常: " + summary);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用Java端LLM服务异常,agentId: {}, 错误: {}", agentId, e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用Java端LLM服务进行智能总结
|
||||||
|
*/
|
||||||
|
private String callJavaLLMForSummary(String conversation, String agentId) {
|
||||||
|
try {
|
||||||
|
// 获取智能体配置,从中提取记忆总结的模型ID
|
||||||
|
String modelId = getMemorySummaryModelId(agentId);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(modelId)) {
|
||||||
|
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
|
||||||
|
return llmService.generateSummary(conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用指定的模型ID调用LLM服务
|
||||||
|
String summary = llmService.generateSummaryWithModel(conversation, modelId);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Java端LLM服务返回异常: " + summary);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用Java端LLM服务异常,agentId: {}, 错误: {}", agentId, e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取记忆总结的LLM模型ID
|
||||||
|
*/
|
||||||
|
private String getMemorySummaryModelId(String agentId) {
|
||||||
|
try {
|
||||||
|
if (StringUtils.isBlank(agentId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取智能体信息
|
||||||
|
AgentInfoVO agentInfo = agentService.getAgentById(agentId);
|
||||||
|
if (agentInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取智能体的记忆模型ID
|
||||||
|
String memModelId = agentInfo.getMemModelId();
|
||||||
|
if (StringUtils.isBlank(memModelId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取记忆模型配置
|
||||||
|
ModelConfigEntity memModelConfig = modelConfigService.getModelByIdFromCache(memModelId);
|
||||||
|
if (memModelConfig == null || memModelConfig.getConfigJson() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从记忆模型配置中提取对应的LLM模型ID
|
||||||
|
Map<String, Object> configMap = memModelConfig.getConfigJson();
|
||||||
|
String llmModelId = (String) configMap.get("llm");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(llmModelId)) {
|
||||||
|
// 如果记忆模型没有配置独立的LLM,则使用智能体的默认LLM模型
|
||||||
|
return agentInfo.getLlmModelId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return llmModelId;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取记忆总结LLM模型ID失败,agentId: {}, 错误: {}", agentId, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据会话ID获取设备信息
|
||||||
|
*/
|
||||||
|
private DeviceEntity getDeviceBySessionId(String sessionId) {
|
||||||
|
try {
|
||||||
|
// 查询该会话的第一条记录获取macAddress
|
||||||
|
QueryWrapper<AgentChatHistoryEntity> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.select("mac_address")
|
||||||
|
.eq("session_id", sessionId)
|
||||||
|
.last("LIMIT 1");
|
||||||
|
|
||||||
|
AgentChatHistoryEntity entity = agentChatHistoryService.getOne(wrapper);
|
||||||
|
if (entity != null && StringUtils.isNotBlank(entity.getMacAddress())) {
|
||||||
|
return deviceService.getDeviceByMacAddress(entity.getMacAddress());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("根据会话ID {} 查找设备信息失败: {}", sessionId, e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -147,6 +147,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
List<String> result = toolsList.stream()
|
List<String> result = toolsList.stream()
|
||||||
.map(tool -> (String) tool.get("name"))
|
.map(tool -> (String) tool.get("name"))
|
||||||
.filter(name -> name != null)
|
.filter(name -> name != null)
|
||||||
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
|
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+67
-31
@@ -5,6 +5,7 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -28,6 +29,7 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
|
|||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.agent.dao.AgentDao;
|
import xiaozhi.modules.agent.dao.AgentDao;
|
||||||
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
||||||
import xiaozhi.modules.agent.dto.AgentDTO;
|
import xiaozhi.modules.agent.dto.AgentDTO;
|
||||||
@@ -42,6 +44,7 @@ import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
|||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||||
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
import xiaozhi.modules.device.service.DeviceService;
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
import xiaozhi.modules.model.dto.VoiceDTO;
|
import xiaozhi.modules.model.dto.VoiceDTO;
|
||||||
@@ -84,17 +87,17 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
|
|
||||||
if (agent.getMemModelId() != null && agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
|
if (agent.getMemModelId() != null && agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
|
||||||
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.IGNORE.getCode());
|
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.IGNORE.getCode());
|
||||||
if (agent.getChatHistoryConf() == null) {
|
|
||||||
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (agent.getChatHistoryConf() == null) {
|
||||||
|
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
// 查询上下文源配置
|
// 查询上下文源配置
|
||||||
AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(id);
|
AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(id);
|
||||||
if (contextProviderEntity != null) {
|
if (contextProviderEntity != null) {
|
||||||
agent.setContextProviders(contextProviderEntity.getContextProviders());
|
agent.setContextProviders(contextProviderEntity.getContextProviders());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无需额外查询插件列表,已通过SQL查询出来
|
// 无需额外查询插件列表,已通过SQL查询出来
|
||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
@@ -127,38 +130,70 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AgentDTO> getUserAgents(Long userId) {
|
public List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType) {
|
||||||
QueryWrapper<AgentEntity> wrapper = new QueryWrapper<>();
|
QueryWrapper<AgentEntity> queryWrapper = new QueryWrapper<>();
|
||||||
wrapper.eq("user_id", userId);
|
queryWrapper.eq("user_id", userId).orderByDesc("created_at");
|
||||||
List<AgentEntity> agents = agentDao.selectList(wrapper);
|
|
||||||
return agents.stream().map(agent -> {
|
|
||||||
AgentDTO dto = new AgentDTO();
|
|
||||||
dto.setId(agent.getId());
|
|
||||||
dto.setAgentName(agent.getAgentName());
|
|
||||||
dto.setSystemPrompt(agent.getSystemPrompt());
|
|
||||||
|
|
||||||
// 获取 TTS 模型名称
|
// 如果有搜索关键词,根据搜索类型添加相应的查询条件
|
||||||
dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId()));
|
if (StringUtils.isNotBlank(keyword)) {
|
||||||
|
if ("mac".equals(searchType)) {
|
||||||
|
// 按MAC地址搜索:先搜索设备,再获取对应的智能体
|
||||||
|
List<DeviceEntity> devices = Optional
|
||||||
|
.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId)).orElseGet(ArrayList::new);
|
||||||
|
// 获取设备对应的智能体ID列表
|
||||||
|
List<String> agentIds = devices.stream()
|
||||||
|
.map(DeviceEntity::getAgentId)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (ToolUtil.isNotEmpty(agentIds)) {
|
||||||
|
queryWrapper.in("id", agentIds);
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 按名称搜索
|
||||||
|
queryWrapper.like("agent_name", keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取 LLM 模型名称
|
// 执行查询
|
||||||
dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId()));
|
List<AgentEntity> agentEntities = baseDao.selectList(queryWrapper);
|
||||||
|
|
||||||
// 获取 VLLM 模型名称
|
// 转换为DTO并设置所有必要字段
|
||||||
dto.setVllmModelName(modelConfigService.getModelNameById(agent.getVllmModelId()));
|
return agentEntities.stream().map(this::buildAgentDTO).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
// 获取记忆模型名称
|
/**
|
||||||
dto.setMemModelId(agent.getMemModelId());
|
* 将AgentEntity转换为AgentDTO
|
||||||
|
*/
|
||||||
|
private AgentDTO buildAgentDTO(AgentEntity agent) {
|
||||||
|
AgentDTO dto = new AgentDTO();
|
||||||
|
dto.setId(agent.getId());
|
||||||
|
dto.setAgentName(agent.getAgentName());
|
||||||
|
dto.setSystemPrompt(agent.getSystemPrompt());
|
||||||
|
|
||||||
// 获取 TTS 音色名称
|
// 获取 TTS 模型名称
|
||||||
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
|
dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId()));
|
||||||
|
|
||||||
// 获取智能体最近的最后连接时长
|
// 获取 LLM 模型名称
|
||||||
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
|
dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId()));
|
||||||
|
|
||||||
// 获取设备数量
|
// 获取 VLLM 模型名称
|
||||||
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
|
dto.setVllmModelName(modelConfigService.getModelNameById(agent.getVllmModelId()));
|
||||||
return dto;
|
|
||||||
}).collect(Collectors.toList());
|
// 获取记忆模型名称
|
||||||
|
dto.setMemModelId(agent.getMemModelId());
|
||||||
|
|
||||||
|
// 获取 TTS 音色名称
|
||||||
|
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
|
||||||
|
|
||||||
|
// 获取智能体最近的最后连接时长
|
||||||
|
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
|
||||||
|
|
||||||
|
// 获取设备数量
|
||||||
|
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
|
||||||
|
|
||||||
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -468,4 +503,5 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
agentPluginMappingService.saveBatch(toInsert);
|
agentPluginMappingService.saveBatch(toInsert);
|
||||||
return entity.getId();
|
return entity.getId();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
+12
-3
@@ -106,6 +106,15 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule) {
|
public Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule) {
|
||||||
|
// 检查是否为管理控制台请求
|
||||||
|
String redisKey = RedisKeys.getTmpRegisterMacKey(macAddress);
|
||||||
|
Object isAdminRequest = redisUtils.get(redisKey);
|
||||||
|
|
||||||
|
if (isAdminRequest != null && "true".equals(isAdminRequest)) {
|
||||||
|
// 管理控制台请求,返回getConfig的结果
|
||||||
|
redisUtils.delete(redisKey); // 使用后清理
|
||||||
|
return (Map<String, Object>) getConfig(true);
|
||||||
|
}
|
||||||
// 根据MAC地址查找设备
|
// 根据MAC地址查找设备
|
||||||
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
@@ -154,11 +163,11 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
}
|
}
|
||||||
result.put("chat_history_conf", chatHistoryConf);
|
result.put("chat_history_conf", chatHistoryConf);
|
||||||
// 如果客户端已实例化模型,则不返回
|
// 如果客户端已实例化模型,则不返回
|
||||||
String alreadySelectedVadModelId = (String) selectedModule.get("VAD");
|
String alreadySelectedVadModelId = selectedModule.get("VAD");
|
||||||
if (alreadySelectedVadModelId != null && alreadySelectedVadModelId.equals(agent.getVadModelId())) {
|
if (alreadySelectedVadModelId != null && alreadySelectedVadModelId.equals(agent.getVadModelId())) {
|
||||||
agent.setVadModelId(null);
|
agent.setVadModelId(null);
|
||||||
}
|
}
|
||||||
String alreadySelectedAsrModelId = (String) selectedModule.get("ASR");
|
String alreadySelectedAsrModelId = selectedModule.get("ASR");
|
||||||
if (alreadySelectedAsrModelId != null && alreadySelectedAsrModelId.equals(agent.getAsrModelId())) {
|
if (alreadySelectedAsrModelId != null && alreadySelectedAsrModelId.equals(agent.getAsrModelId())) {
|
||||||
agent.setAsrModelId(null);
|
agent.setAsrModelId(null);
|
||||||
}
|
}
|
||||||
@@ -293,7 +302,7 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
|
private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
|
||||||
try {
|
try {
|
||||||
// 获取声纹接口地址
|
// 获取声纹接口地址
|
||||||
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
|
String voiceprintUrl = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
|
||||||
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
|
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-85
@@ -1,14 +1,11 @@
|
|||||||
package xiaozhi.modules.device.controller;
|
package xiaozhi.modules.device.controller;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -16,9 +13,6 @@ import org.springframework.web.bind.annotation.PutMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -30,6 +24,7 @@ import xiaozhi.common.user.UserDetail;
|
|||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceRegisterDTO;
|
import xiaozhi.modules.device.dto.DeviceRegisterDTO;
|
||||||
|
import xiaozhi.modules.device.dto.DeviceToolsCallReqDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceUpdateDTO;
|
import xiaozhi.modules.device.dto.DeviceUpdateDTO;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
@@ -44,16 +39,11 @@ public class DeviceController {
|
|||||||
private final DeviceService deviceService;
|
private final DeviceService deviceService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final SysParamsService sysParamsService;
|
private final SysParamsService sysParamsService;
|
||||||
private final RestTemplate restTemplate;
|
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
public DeviceController(DeviceService deviceService, RedisUtils redisUtils, SysParamsService sysParamsService,
|
public DeviceController(DeviceService deviceService, RedisUtils redisUtils, SysParamsService sysParamsService) {
|
||||||
RestTemplate restTemplate, ObjectMapper objectMapper) {
|
|
||||||
this.deviceService = deviceService;
|
this.deviceService = deviceService;
|
||||||
this.redisUtils = redisUtils;
|
this.redisUtils = redisUtils;
|
||||||
this.sysParamsService = sysParamsService;
|
this.sysParamsService = sysParamsService;
|
||||||
this.restTemplate = restTemplate;
|
|
||||||
this.objectMapper = objectMapper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/bind/{agentId}/{deviceCode}")
|
@PostMapping("/bind/{agentId}/{deviceCode}")
|
||||||
@@ -99,83 +89,12 @@ public class DeviceController {
|
|||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<String> forwardToMqttGateway(@PathVariable String agentId, @RequestBody String requestBody) {
|
public Result<String> forwardToMqttGateway(@PathVariable String agentId, @RequestBody String requestBody) {
|
||||||
try {
|
try {
|
||||||
// 从系统参数中获取MQTT网关地址
|
return new Result<String>().ok(deviceService.getDeviceOnlineData(agentId));
|
||||||
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
|
||||||
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
|
||||||
return new Result<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前用户的设备列表
|
|
||||||
UserDetail user = SecurityUser.getUser();
|
|
||||||
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId(), agentId);
|
|
||||||
|
|
||||||
// 构建deviceIds数组
|
|
||||||
java.util.List<String> deviceIds = new java.util.ArrayList<>();
|
|
||||||
for (DeviceEntity device : devices) {
|
|
||||||
String macAddress = device.getMacAddress() != null ? device.getMacAddress() : "unknown";
|
|
||||||
String groupId = device.getBoard() != null ? device.getBoard() : "GID_default";
|
|
||||||
|
|
||||||
// 替换冒号为下划线
|
|
||||||
groupId = groupId.replace(":", "_");
|
|
||||||
macAddress = macAddress.replace(":", "_");
|
|
||||||
|
|
||||||
// 构建mqtt客户端ID格式:groupId@@@macAddress@@@macAddress
|
|
||||||
String mqttClientId = groupId + "@@@" + macAddress + "@@@" + macAddress;
|
|
||||||
deviceIds.add(mqttClientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建完整的URL
|
|
||||||
String url = "http://" + mqttGatewayUrl + "/api/devices/status";
|
|
||||||
|
|
||||||
// 设置请求头
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set("Content-Type", "application/json");
|
|
||||||
|
|
||||||
// 生成Bearer令牌
|
|
||||||
String token = generateBearerToken();
|
|
||||||
if (token == null) {
|
|
||||||
return new Result<String>().error("令牌生成失败");
|
|
||||||
}
|
|
||||||
headers.set("Authorization", "Bearer " + token);
|
|
||||||
|
|
||||||
// 构建请求体JSON
|
|
||||||
String jsonBody = "{\"clientIds\":" + objectMapper.writeValueAsString(deviceIds) + "}";
|
|
||||||
HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
|
|
||||||
|
|
||||||
// 发送POST请求
|
|
||||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
|
|
||||||
|
|
||||||
// 返回响应
|
|
||||||
return new Result<String>().ok(response.getBody());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return new Result<String>().error("转发请求失败: " + e.getMessage());
|
return new Result<String>().error("转发请求失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateBearerToken() {
|
|
||||||
try {
|
|
||||||
// 获取当前日期,格式为yyyy-MM-dd
|
|
||||||
String dateStr = java.time.LocalDate.now()
|
|
||||||
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
||||||
|
|
||||||
// 获取MQTT签名密钥
|
|
||||||
String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", false);
|
|
||||||
if (StringUtils.isBlank(signatureKey)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将日期字符串与MQTT_SIGNATURE_KEY连接
|
|
||||||
String tokenContent = dateStr + signatureKey;
|
|
||||||
|
|
||||||
// 对连接后的字符串进行SHA256哈希计算
|
|
||||||
String token = org.apache.commons.codec.digest.DigestUtils.sha256Hex(tokenContent);
|
|
||||||
|
|
||||||
return token;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/unbind")
|
@PostMapping("/unbind")
|
||||||
@Operation(summary = "解绑设备")
|
@Operation(summary = "解绑设备")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
@@ -210,4 +129,34 @@ public class DeviceController {
|
|||||||
deviceService.manualAddDevice(user.getId(), dto);
|
deviceService.manualAddDevice(user.getId(), dto);
|
||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/tools/list/{deviceId}")
|
||||||
|
@Operation(summary = "获取设备工具列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Object> getDeviceTools(@PathVariable String deviceId) {
|
||||||
|
Object toolsData = deviceService.getDeviceTools(deviceId);
|
||||||
|
if (toolsData == null) {
|
||||||
|
return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result<Object>().ok(toolsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/tools/call/{deviceId}")
|
||||||
|
@Operation(summary = "调用设备工具")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Object> callDeviceTool(@PathVariable String deviceId,
|
||||||
|
@Valid @RequestBody DeviceToolsCallReqDTO request) {
|
||||||
|
String toolName = request.getName();
|
||||||
|
Map<String, Object> arguments = request.getArguments();
|
||||||
|
|
||||||
|
Object result = deviceService.callDeviceTool(deviceId, toolName, arguments);
|
||||||
|
if (result == null) {
|
||||||
|
return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Object> response = new Result<Object>();
|
||||||
|
response.setMsg("Tools called successfully");
|
||||||
|
return response.ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+59
-9
@@ -7,7 +7,9 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -36,6 +38,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.redis.RedisKeys;
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
@@ -43,8 +46,11 @@ import xiaozhi.common.utils.Result;
|
|||||||
import xiaozhi.common.validator.ValidatorUtils;
|
import xiaozhi.common.validator.ValidatorUtils;
|
||||||
import xiaozhi.modules.device.entity.OtaEntity;
|
import xiaozhi.modules.device.entity.OtaEntity;
|
||||||
import xiaozhi.modules.device.service.OtaService;
|
import xiaozhi.modules.device.service.OtaService;
|
||||||
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
|
||||||
@Tag(name = "设备管理", description = "OTA 相关接口")
|
@Tag(name = "固件升级管理", description = "OTA 相关接口")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -53,6 +59,7 @@ public class OTAMagController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(OTAController.class);
|
private static final Logger logger = LoggerFactory.getLogger(OTAController.class);
|
||||||
private final OtaService otaService;
|
private final OtaService otaService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
|
private final SysParamsService sysParamsService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Operation(summary = "分页查询 OTA 固件信息")
|
@Operation(summary = "分页查询 OTA 固件信息")
|
||||||
@@ -145,15 +152,11 @@ public class OTAMagController {
|
|||||||
|
|
||||||
// 检查下载次数
|
// 检查下载次数
|
||||||
String downloadCountKey = RedisKeys.getOtaDownloadCountKey(uuid);
|
String downloadCountKey = RedisKeys.getOtaDownloadCountKey(uuid);
|
||||||
Integer downloadCount = (Integer) redisUtils.get(downloadCountKey);
|
Integer downloadCount = (Integer) Optional.ofNullable(redisUtils.get(downloadCountKey)).orElse(0);
|
||||||
if (downloadCount == null) {
|
|
||||||
downloadCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果下载次数超过3次,返回404
|
// 如果下载次数超过3次,返回404
|
||||||
if (downloadCount >= 3) {
|
if (downloadCount >= 3) {
|
||||||
redisUtils.delete(downloadCountKey);
|
redisUtils.delete(List.of(downloadCountKey, RedisKeys.getOtaIdKey(uuid)));
|
||||||
redisUtils.delete(RedisKeys.getOtaIdKey(uuid));
|
|
||||||
logger.warn("Download limit exceeded for UUID: {}", uuid);
|
logger.warn("Download limit exceeded for UUID: {}", uuid);
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
@@ -162,7 +165,17 @@ public class OTAMagController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取固件信息
|
// 获取固件信息
|
||||||
OtaEntity otaEntity = otaService.selectById(id);
|
OtaEntity otaEntity = null;
|
||||||
|
if (id.indexOf("file:") == 0) {
|
||||||
|
id = id.substring(5);
|
||||||
|
otaEntity = new OtaEntity();
|
||||||
|
otaEntity.setFirmwarePath(id);
|
||||||
|
otaEntity.setType("assets");
|
||||||
|
otaEntity.setVersion("1.0.0");
|
||||||
|
} else {
|
||||||
|
otaEntity = otaService.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
if (otaEntity == null || StringUtils.isBlank(otaEntity.getFirmwarePath())) {
|
if (otaEntity == null || StringUtils.isBlank(otaEntity.getFirmwarePath())) {
|
||||||
logger.warn("Firmware not found or path is empty for ID: {}", id);
|
logger.warn("Firmware not found or path is empty for ID: {}", id);
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
@@ -170,6 +183,7 @@ public class OTAMagController {
|
|||||||
|
|
||||||
// 获取文件路径 - 确保路径是绝对路径或正确的相对路径
|
// 获取文件路径 - 确保路径是绝对路径或正确的相对路径
|
||||||
String firmwarePath = otaEntity.getFirmwarePath();
|
String firmwarePath = otaEntity.getFirmwarePath();
|
||||||
|
String originalFilename = otaEntity.getType() + "_" + otaEntity.getVersion();
|
||||||
Path path;
|
Path path;
|
||||||
|
|
||||||
// 检查是否是绝对路径
|
// 检查是否是绝对路径
|
||||||
@@ -203,7 +217,7 @@ public class OTAMagController {
|
|||||||
byte[] fileContent = Files.readAllBytes(path);
|
byte[] fileContent = Files.readAllBytes(path);
|
||||||
|
|
||||||
// 设置响应头
|
// 设置响应头
|
||||||
String originalFilename = otaEntity.getType() + "_" + otaEntity.getVersion();
|
|
||||||
if (firmwarePath.contains(".")) {
|
if (firmwarePath.contains(".")) {
|
||||||
String extension = firmwarePath.substring(firmwarePath.lastIndexOf("."));
|
String extension = firmwarePath.substring(firmwarePath.lastIndexOf("."));
|
||||||
originalFilename += extension;
|
originalFilename += extension;
|
||||||
@@ -279,6 +293,42 @@ public class OTAMagController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/uploadAssetsBin")
|
||||||
|
@Operation(summary = "上传资源固件文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<String> uploadAssetsBin(@RequestParam("file") MultipartFile file) {
|
||||||
|
String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
|
||||||
|
if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
|
||||||
|
return new Result<String>().error(ErrorCode.OTA_URL_EMPTY);
|
||||||
|
}
|
||||||
|
logger.info("username:{},uploadAssetsBin size: {}", SecurityUser.getUser().getUsername(), file.getSize());
|
||||||
|
// 验证文件大小 (资源固件最大20MB)
|
||||||
|
if (file.getSize() > 20 * 1024 * 1024) {
|
||||||
|
return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
|
||||||
|
}
|
||||||
|
// 普通用户只能每天上传50次
|
||||||
|
if (SecurityUser.getUser().getSuperAdmin() == SuperAdminEnum.NO.value()) {
|
||||||
|
String uploadCountKey = RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId());
|
||||||
|
Integer uploadCount = (Integer) Optional.ofNullable(redisUtils.get(uploadCountKey)).orElse(0);
|
||||||
|
if (uploadCount >= 50) {
|
||||||
|
return new Result<String>().error(ErrorCode.OTA_UPLOAD_COUNT_EXCEED);
|
||||||
|
}
|
||||||
|
// 增加上传次数
|
||||||
|
redisUtils.increment(RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId()),
|
||||||
|
RedisUtils.DEFAULT_EXPIRE);
|
||||||
|
}
|
||||||
|
Result<String> result = uploadFirmware(file);
|
||||||
|
|
||||||
|
// 生成资源文件路径
|
||||||
|
if (StringUtils.isNotBlank(result.getData())) {
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
redisUtils.set(RedisKeys.getOtaIdKey(uuid), "file:" + result.getData());
|
||||||
|
String downloadUrl = otaUrl.replace("/ota/", "/otaMag/download/") + uuid;
|
||||||
|
result.setData(downloadUrl);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private String calculateMD5(MultipartFile file) throws IOException, NoSuchAlgorithmException {
|
private String calculateMD5(MultipartFile file) throws IOException, NoSuchAlgorithmException {
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
byte[] digest = md.digest(file.getBytes());
|
byte[] digest = md.digest(file.getBytes());
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package xiaozhi.modules.device.dto;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class DeviceToolsCallReqDTO {
|
||||||
|
|
||||||
|
@NotBlank(message = "工具名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Map<String, Object> arguments;
|
||||||
|
}
|
||||||
@@ -2,17 +2,22 @@ package xiaozhi.modules.device.service;
|
|||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.service.BaseService;
|
import xiaozhi.common.service.BaseService;
|
||||||
|
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||||
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
import xiaozhi.modules.device.vo.UserShowDeviceListVO;
|
import xiaozhi.modules.device.vo.UserShowDeviceListVO;
|
||||||
|
|
||||||
public interface DeviceService extends BaseService<DeviceEntity> {
|
public interface DeviceService extends BaseService<DeviceEntity> {
|
||||||
|
/**
|
||||||
|
* 获取设备在线数据
|
||||||
|
*/
|
||||||
|
String getDeviceOnlineData(String agentId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查设备是否激活
|
* 检查设备是否激活
|
||||||
@@ -83,6 +88,7 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取这个智能体设备理的最近的最后连接时间
|
* 获取这个智能体设备理的最近的最后连接时间
|
||||||
|
*
|
||||||
* @param agentId 智能体id
|
* @param agentId 智能体id
|
||||||
* @return 返回设备最近的最后连接时间
|
* @return 返回设备最近的最后连接时间
|
||||||
*/
|
*/
|
||||||
@@ -98,4 +104,33 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
|||||||
*/
|
*/
|
||||||
void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion);
|
void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成WebSocket认证token
|
||||||
|
*
|
||||||
|
* @param clientId 客户端ID
|
||||||
|
* @param username 用户名(通常为deviceId)
|
||||||
|
* @return 认证token字符串
|
||||||
|
* @throws Exception 生成token时的异常
|
||||||
|
*/
|
||||||
|
String generateWebSocketToken(String clientId, String username) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据MAC地址搜索设备
|
||||||
|
*
|
||||||
|
* @param macAddress MAC地址关键词
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 设备列表
|
||||||
|
*/
|
||||||
|
List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备工具列表
|
||||||
|
*/
|
||||||
|
Object getDeviceTools(String deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用设备工具
|
||||||
|
*/
|
||||||
|
Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments);
|
||||||
|
|
||||||
}
|
}
|
||||||
+246
-17
@@ -9,8 +9,11 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -26,7 +29,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
|
import cn.hutool.http.ContentType;
|
||||||
|
import cn.hutool.http.Header;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -40,6 +52,7 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
|
|||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.device.dao.DeviceDao;
|
import xiaozhi.modules.device.dao.DeviceDao;
|
||||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||||
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
||||||
@@ -88,16 +101,16 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
if (StringUtils.isBlank(activationCode)) {
|
if (StringUtils.isBlank(activationCode)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
|
||||||
}
|
}
|
||||||
String deviceKey = "ota:activation:code:" + activationCode;
|
String deviceKey = RedisKeys.getOtaActivationCode(activationCode);
|
||||||
Object cacheDeviceId = redisUtils.get(deviceKey);
|
Object cacheDeviceId = redisUtils.get(deviceKey);
|
||||||
if (cacheDeviceId == null) {
|
if (ToolUtil.isEmpty(cacheDeviceId)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
||||||
}
|
}
|
||||||
String deviceId = (String) cacheDeviceId;
|
String deviceId = (String) cacheDeviceId;
|
||||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||||
String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId);
|
String cacheDeviceKey = RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
|
||||||
Map<String, Object> cacheMap = (Map<String, Object>) redisUtils.get(cacheDeviceKey);
|
Map<String, Object> cacheMap = (Map<String, Object>) redisUtils.get(cacheDeviceKey);
|
||||||
if (cacheMap == null) {
|
if (ToolUtil.isEmpty(cacheMap)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
||||||
}
|
}
|
||||||
String cachedCode = (String) cacheMap.get("activation_code");
|
String cachedCode = (String) cacheMap.get("activation_code");
|
||||||
@@ -133,19 +146,56 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
deviceEntity.setLastConnectedAt(currentTime);
|
deviceEntity.setLastConnectedAt(currentTime);
|
||||||
deviceDao.insert(deviceEntity);
|
deviceDao.insert(deviceEntity);
|
||||||
|
|
||||||
// 清理redis缓存
|
// 清理redis缓存、清除智能体设备数量缓存
|
||||||
redisUtils.delete(cacheDeviceKey);
|
redisUtils.delete(List.of(cacheDeviceKey, deviceKey, RedisKeys.getAgentDeviceCountById(agentId)));
|
||||||
redisUtils.delete(deviceKey);
|
|
||||||
|
|
||||||
// 添加:清除智能体设备数量缓存
|
|
||||||
redisUtils.delete(RedisKeys.getAgentDeviceCountById(agentId));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备在线数据
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
|
public String getDeviceOnlineData(String agentId) {
|
||||||
DeviceReportReqDTO deviceReport) {
|
// 从系统参数中获取MQTT网关地址
|
||||||
|
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||||
|
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// 构建完整的URL
|
||||||
|
String url = StrUtil.format("http://{}/api/devices/status", mqttGatewayUrl);
|
||||||
|
|
||||||
|
// 获取当前用户的设备列表
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
List<DeviceEntity> devices = getUserDevices(user.getId(), agentId);
|
||||||
|
|
||||||
|
// 构建deviceIds数组
|
||||||
|
Set<String> deviceIds = devices.stream().map(o -> {
|
||||||
|
String macAddress = Optional.ofNullable(o.getMacAddress()).orElse("unknown").replace(":", "_");
|
||||||
|
String groupId = Optional.ofNullable(o.getBoard()).orElse("GID_default").replace(":", "_");
|
||||||
|
return StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
|
||||||
|
}).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 构建请求入参
|
||||||
|
Map<String, Set<String>> params = MapUtil
|
||||||
|
.builder(new HashMap<String, Set<String>>())
|
||||||
|
.put("clientIds", deviceIds).build();
|
||||||
|
|
||||||
|
if (ToolUtil.isNotEmpty(deviceIds)) {
|
||||||
|
// 发送请求
|
||||||
|
String resultMessage = HttpRequest.post(url)
|
||||||
|
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||||
|
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||||
|
.body(JSONUtil.toJsonStr(params))
|
||||||
|
.timeout(10000) // 超时,毫秒
|
||||||
|
.execute().body();
|
||||||
|
return resultMessage;
|
||||||
|
}
|
||||||
|
// 返回响应
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId, DeviceReportReqDTO deviceReport) {
|
||||||
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
||||||
response.setServer_time(buildServerTime());
|
response.setServer_time(buildServerTime());
|
||||||
|
|
||||||
@@ -356,8 +406,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
|
|
||||||
private String getDeviceCacheKey(String deviceId) {
|
private String getDeviceCacheKey(String deviceId) {
|
||||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||||
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
return RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
|
||||||
return dataKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceReportRespDTO.Activation buildActivation(String deviceId, DeviceReportReqDTO deviceReport) {
|
public DeviceReportRespDTO.Activation buildActivation(String deviceId, DeviceReportReqDTO deviceReport) {
|
||||||
@@ -396,7 +445,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
redisUtils.set(dataKey, dataMap);
|
redisUtils.set(dataKey, dataMap);
|
||||||
|
|
||||||
// 写入反查激活码 key
|
// 写入反查激活码 key
|
||||||
String codeKey = "ota:activation:code:" + newCode;
|
String codeKey = RedisKeys.getOtaActivationCode(newCode);
|
||||||
redisUtils.set(codeKey, deviceId);
|
redisUtils.set(codeKey, deviceId);
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
@@ -496,6 +545,14 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
redisUtils.delete(RedisKeys.getAgentDeviceCountById(dto.getAgentId()));
|
redisUtils.delete(RedisKeys.getAgentDeviceCountById(dto.getAgentId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId) {
|
||||||
|
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
|
||||||
|
wrapper.like("mac_address", macAddress);
|
||||||
|
wrapper.eq("user_id", userId);
|
||||||
|
return deviceDao.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成MQTT密码签名
|
* 生成MQTT密码签名
|
||||||
*
|
*
|
||||||
@@ -518,7 +575,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
* @param username 用户名 (通常为deviceId/macAddress)
|
* @param username 用户名 (通常为deviceId/macAddress)
|
||||||
* @return 认证token字符串
|
* @return 认证token字符串
|
||||||
*/
|
*/
|
||||||
private String generateWebSocketToken(String clientId, String username)
|
public String generateWebSocketToken(String clientId, String username)
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||||
// 从系统参数获取密钥
|
// 从系统参数获取密钥
|
||||||
String secretKey = sysParamsService.getValue(Constant.SERVER_SECRET, false);
|
String secretKey = sysParamsService.getValue(Constant.SERVER_SECRET, false);
|
||||||
@@ -598,4 +655,176 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
|
|
||||||
return mqtt;
|
return mqtt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成BearerToken
|
||||||
|
*/
|
||||||
|
private String generateBearerToken() {
|
||||||
|
try {
|
||||||
|
String dateStr = DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
|
||||||
|
String signatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET, false);
|
||||||
|
if (ToolUtil.isEmpty(signatureKey)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DigestUtil.sha256Hex(dateStr + signatureKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDeviceTools(String deviceId) {
|
||||||
|
// 从系统参数中获取MQTT网关地址
|
||||||
|
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||||
|
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备信息
|
||||||
|
DeviceEntity device = baseDao.selectById(deviceId);
|
||||||
|
if (device == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查设备是否属于当前用户
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
if (!device.getUserId().equals(user.getId())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建clientId
|
||||||
|
String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
|
||||||
|
String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
|
||||||
|
String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
|
||||||
|
|
||||||
|
// 构建完整的URL
|
||||||
|
String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
|
||||||
|
|
||||||
|
// 构建请求体
|
||||||
|
Map<String, Object> payload = MapUtil
|
||||||
|
.builder(new HashMap<String, Object>())
|
||||||
|
.put("jsonrpc", "2.0")
|
||||||
|
.put("id", 2)
|
||||||
|
.put("method", "tools/list")
|
||||||
|
.put("params", MapUtil.builder(new HashMap<String, Object>())
|
||||||
|
.put("withUserTools", true)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, Object> requestBody = MapUtil
|
||||||
|
.builder(new HashMap<String, Object>())
|
||||||
|
.put("type", "mcp")
|
||||||
|
.put("payload", payload)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
String resultMessage = HttpRequest.post(url)
|
||||||
|
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||||
|
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||||
|
.body(JSONUtil.toJsonStr(requestBody))
|
||||||
|
.timeout(10000) // 超时,毫秒
|
||||||
|
.execute().body();
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
if (StringUtils.isNotBlank(resultMessage)) {
|
||||||
|
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
|
||||||
|
if (jsonObject.getBool("success", false)) {
|
||||||
|
return jsonObject.get("data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments) {
|
||||||
|
// 从系统参数中获取MQTT网关地址
|
||||||
|
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||||
|
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备信息
|
||||||
|
DeviceEntity device = baseDao.selectById(deviceId);
|
||||||
|
if (device == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查设备是否属于当前用户
|
||||||
|
UserDetail user = SecurityUser.getUser();
|
||||||
|
if (!device.getUserId().equals(user.getId())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建clientId
|
||||||
|
String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
|
||||||
|
String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
|
||||||
|
String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
|
||||||
|
|
||||||
|
// 构建完整的URL
|
||||||
|
String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
|
||||||
|
|
||||||
|
// 构建请求体
|
||||||
|
Map<String, Object> params = MapUtil
|
||||||
|
.builder(new HashMap<String, Object>())
|
||||||
|
.put("name", toolName)
|
||||||
|
.put("arguments", arguments)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, Object> payload = MapUtil
|
||||||
|
.builder(new HashMap<String, Object>())
|
||||||
|
.put("jsonrpc", "2.0")
|
||||||
|
.put("id", 2)
|
||||||
|
.put("method", "tools/call")
|
||||||
|
.put("params", params)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, Object> requestBody = MapUtil
|
||||||
|
.builder(new HashMap<String, Object>())
|
||||||
|
.put("type", "mcp")
|
||||||
|
.put("payload", payload)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
String resultMessage = HttpRequest.post(url)
|
||||||
|
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||||
|
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||||
|
.body(JSONUtil.toJsonStr(requestBody))
|
||||||
|
.timeout(10000) // 超时,毫秒
|
||||||
|
.execute().body();
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
if (StringUtils.isNotBlank(resultMessage)) {
|
||||||
|
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
|
||||||
|
if (jsonObject.getBool("success", false)) {
|
||||||
|
cn.hutool.json.JSONObject data = jsonObject.getJSONObject("data");
|
||||||
|
if (data != null) {
|
||||||
|
cn.hutool.json.JSONArray content = data.getJSONArray("content");
|
||||||
|
if (content != null && content.size() > 0) {
|
||||||
|
cn.hutool.json.JSONObject firstContent = content.getJSONObject(0);
|
||||||
|
if (firstContent != null && "text".equals(firstContent.getStr("type"))) {
|
||||||
|
String text = firstContent.getStr("text");
|
||||||
|
if (StringUtils.isNotBlank(text)) {
|
||||||
|
String trimmedText = text.trim();
|
||||||
|
if (trimmedText.startsWith("{") || trimmedText.startsWith("[")) {
|
||||||
|
try {
|
||||||
|
return JSONUtil.parseObj(trimmedText);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return trimmedText;
|
||||||
|
}
|
||||||
|
} else if ("true".equals(trimmedText)) {
|
||||||
|
return true;
|
||||||
|
} else if ("false".equals(trimmedText)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return trimmedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-16
@@ -1,7 +1,6 @@
|
|||||||
package xiaozhi.modules.knowledge.controller;
|
package xiaozhi.modules.knowledge.controller;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
@@ -24,8 +23,10 @@ import xiaozhi.common.exception.ErrorCode;
|
|||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
||||||
import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
|
import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
|
||||||
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
import xiaozhi.modules.security.user.SecurityUser;
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@@ -131,20 +132,17 @@ public class KnowledgeBaseController {
|
|||||||
|
|
||||||
// 获取当前登录用户ID
|
// 获取当前登录用户ID
|
||||||
Long currentUserId = SecurityUser.getUserId();
|
Long currentUserId = SecurityUser.getUserId();
|
||||||
String[] idArray = ids.split(",");
|
List<String> idList = Arrays.asList(ids.split(","));
|
||||||
for (String datasetId : idArray) {
|
List<KnowledgeBaseDTO> knowledgeBaseDTOs = Optional.ofNullable(knowledgeBaseService.getByDatasetIdList(idList)).orElseGet(ArrayList::new);
|
||||||
if (StringUtils.isNotBlank(datasetId)) {
|
if (ToolUtil.isNotEmpty(knowledgeBaseDTOs)) {
|
||||||
// 先获取现有知识库信息以检查权限
|
knowledgeBaseDTOs.forEach(item->{
|
||||||
KnowledgeBaseDTO existingKnowledgeBase = knowledgeBaseService.getByDatasetId(datasetId.trim());
|
|
||||||
|
|
||||||
// 检查权限:用户只能删除自己创建的知识库
|
// 检查权限:用户只能删除自己创建的知识库
|
||||||
if (existingKnowledgeBase.getCreator() == null
|
if (item.getCreator() == null || !item.getCreator().equals(currentUserId)) {
|
||||||
|| !existingKnowledgeBase.getCreator().equals(currentUserId)) {
|
|
||||||
throw new RenException(ErrorCode.NO_PERMISSION);
|
throw new RenException(ErrorCode.NO_PERMISSION);
|
||||||
}
|
}
|
||||||
|
//删除
|
||||||
knowledgeBaseService.deleteByDatasetId(datasetId.trim());
|
knowledgeBaseService.deleteByDatasetId(item.getDatasetId());
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
@@ -152,8 +150,8 @@ public class KnowledgeBaseController {
|
|||||||
@GetMapping("/rag-models")
|
@GetMapping("/rag-models")
|
||||||
@Operation(summary = "获取RAG模型列表")
|
@Operation(summary = "获取RAG模型列表")
|
||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<List<Map<String, Object>>> getRAGModels() {
|
public Result<List<ModelConfigEntity>> getRAGModels() {
|
||||||
List<Map<String, Object>> result = knowledgeBaseService.getRAGModels();
|
List<ModelConfigEntity> result = knowledgeBaseService.getRAGModels();
|
||||||
return new Result<List<Map<String, Object>>>().ok(result);
|
return new Result<List<ModelConfigEntity>>().ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-11
@@ -63,16 +63,11 @@ public class KnowledgeFilesController {
|
|||||||
// 验证知识库权限
|
// 验证知识库权限
|
||||||
validateKnowledgeBasePermission(datasetId);
|
validateKnowledgeBasePermission(datasetId);
|
||||||
|
|
||||||
// 如果指定了状态参数,使用状态查询接口
|
//组装参数
|
||||||
if (status != null) {
|
|
||||||
PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageListByStatus(datasetId, status, page, page_size);
|
|
||||||
return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 否则使用通用查询接口
|
|
||||||
KnowledgeFilesDTO knowledgeFilesDTO = new KnowledgeFilesDTO();
|
KnowledgeFilesDTO knowledgeFilesDTO = new KnowledgeFilesDTO();
|
||||||
knowledgeFilesDTO.setDatasetId(datasetId);
|
knowledgeFilesDTO.setDatasetId(datasetId);
|
||||||
knowledgeFilesDTO.setName(name);
|
knowledgeFilesDTO.setName(name);
|
||||||
|
knowledgeFilesDTO.setStatus(status);
|
||||||
PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageList(knowledgeFilesDTO, page, page_size);
|
PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageList(knowledgeFilesDTO, page, page_size);
|
||||||
return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
|
return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
|
||||||
}
|
}
|
||||||
@@ -87,8 +82,11 @@ public class KnowledgeFilesController {
|
|||||||
@RequestParam(required = false, defaultValue = "10") Integer page_size) {
|
@RequestParam(required = false, defaultValue = "10") Integer page_size) {
|
||||||
// 验证知识库权限
|
// 验证知识库权限
|
||||||
validateKnowledgeBasePermission(datasetId);
|
validateKnowledgeBasePermission(datasetId);
|
||||||
|
//组装参数
|
||||||
PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageListByStatus(datasetId, status, page, page_size);
|
KnowledgeFilesDTO knowledgeFilesDTO = new KnowledgeFilesDTO();
|
||||||
|
knowledgeFilesDTO.setDatasetId(datasetId);
|
||||||
|
knowledgeFilesDTO.setStatus(status);
|
||||||
|
PageData<KnowledgeFilesDTO> pageData = knowledgeFilesService.getPageList(knowledgeFilesDTO, page, page_size);
|
||||||
return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
|
return new Result<PageData<KnowledgeFilesDTO>>().ok(pageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +157,7 @@ public class KnowledgeFilesController {
|
|||||||
// 验证知识库权限
|
// 验证知识库权限
|
||||||
validateKnowledgeBasePermission(datasetId);
|
validateKnowledgeBasePermission(datasetId);
|
||||||
|
|
||||||
Map<String, Object> result = knowledgeFilesService.listChunks(datasetId, documentId, keywords, page, page_size,
|
Map<String, Object> result = knowledgeFilesService.listChunks(datasetId, documentId, keywords, page, page_size, id);
|
||||||
id);
|
|
||||||
return new Result<Map<String, Object>>().ok(result);
|
return new Result<Map<String, Object>>().ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -7,6 +7,7 @@ import xiaozhi.common.page.PageData;
|
|||||||
import xiaozhi.common.service.BaseService;
|
import xiaozhi.common.service.BaseService;
|
||||||
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
||||||
import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
|
import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
|
||||||
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库知识库服务接口
|
* 知识库知识库服务接口
|
||||||
@@ -55,6 +56,14 @@ public interface KnowledgeBaseService extends BaseService<KnowledgeBaseEntity> {
|
|||||||
*/
|
*/
|
||||||
KnowledgeBaseDTO getByDatasetId(String datasetId);
|
KnowledgeBaseDTO getByDatasetId(String datasetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据知识库ID集合查询知识库
|
||||||
|
*
|
||||||
|
* @param datasetIdList 知识库ID集合
|
||||||
|
* @return 知识库详情
|
||||||
|
*/
|
||||||
|
List<KnowledgeBaseDTO> getByDatasetIdList(List<String> datasetIdList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据知识库ID删除知识库
|
* 根据知识库ID删除知识库
|
||||||
*
|
*
|
||||||
@@ -83,5 +92,5 @@ public interface KnowledgeBaseService extends BaseService<KnowledgeBaseEntity> {
|
|||||||
*
|
*
|
||||||
* @return RAG模型列表
|
* @return RAG模型列表
|
||||||
*/
|
*/
|
||||||
List<Map<String, Object>> getRAGModels();
|
List<ModelConfigEntity> getRAGModels();
|
||||||
}
|
}
|
||||||
-11
@@ -47,17 +47,6 @@ public interface KnowledgeFilesService {
|
|||||||
Map<String, Object> metaFields, String chunkMethod,
|
Map<String, Object> metaFields, String chunkMethod,
|
||||||
Map<String, Object> parserConfig);
|
Map<String, Object> parserConfig);
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据状态分页查询文档列表
|
|
||||||
*
|
|
||||||
* @param datasetId 知识库ID
|
|
||||||
* @param status 文档解析状态(0-未开始,1-进行中,2-已取消,3-已完成,4-失败)
|
|
||||||
* @param page 页码
|
|
||||||
* @param limit 每页数量
|
|
||||||
* @return 分页数据
|
|
||||||
*/
|
|
||||||
PageData<KnowledgeFilesDTO> getPageListByStatus(String datasetId, Integer status, Integer page, Integer limit);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文档ID和知识库ID删除文档
|
* 根据文档ID和知识库ID删除文档
|
||||||
*
|
*
|
||||||
|
|||||||
+26
-23
@@ -1,21 +1,12 @@
|
|||||||
package xiaozhi.modules.knowledge.service.impl;
|
package xiaozhi.modules.knowledge.service.impl;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
@@ -32,6 +23,7 @@ import xiaozhi.common.redis.RedisUtils;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.MessageUtils;
|
import xiaozhi.common.utils.MessageUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao;
|
import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao;
|
||||||
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
||||||
import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
|
import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
|
||||||
@@ -53,8 +45,6 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao,
|
|||||||
private final ModelConfigService modelConfigService;
|
private final ModelConfigService modelConfigService;
|
||||||
private final ModelConfigDao modelConfigDao;
|
private final ModelConfigDao modelConfigDao;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private RestTemplate restTemplate = new RestTemplate();
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KnowledgeBaseEntity selectById(Serializable datasetId) {
|
public KnowledgeBaseEntity selectById(Serializable datasetId) {
|
||||||
@@ -269,6 +259,26 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao,
|
|||||||
return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
|
return ConvertUtils.sourceToTarget(entity, KnowledgeBaseDTO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据知识库ID集合查询知识库
|
||||||
|
* @param datasetIdList 知识库ID集合
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<KnowledgeBaseDTO> getByDatasetIdList(List<String> datasetIdList) {
|
||||||
|
//判断参数
|
||||||
|
if (ToolUtil.isEmpty(datasetIdList)) {
|
||||||
|
throw new RenException(ErrorCode.PARAMS_GET_ERROR);
|
||||||
|
}
|
||||||
|
//批量查询
|
||||||
|
List<KnowledgeBaseEntity> entityList = knowledgeBaseDao.selectList(
|
||||||
|
new QueryWrapper<KnowledgeBaseEntity>().in("dataset_id", datasetIdList));
|
||||||
|
if (ToolUtil.isEmpty(entityList)) {
|
||||||
|
throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
return ConvertUtils.sourceToTarget(entityList, KnowledgeBaseDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteByDatasetId(String datasetId) {
|
public void deleteByDatasetId(String datasetId) {
|
||||||
if (StringUtils.isBlank(datasetId)) {
|
if (StringUtils.isBlank(datasetId)) {
|
||||||
@@ -380,24 +390,17 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Map<String, Object>> getRAGModels() {
|
public List<ModelConfigEntity> getRAGModels() {
|
||||||
// 查询RAG类型的模型配置
|
// 查询RAG类型的模型配置
|
||||||
QueryWrapper<ModelConfigEntity> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<ModelConfigEntity> queryWrapper = new QueryWrapper<ModelConfigEntity>()
|
||||||
queryWrapper.eq("model_type", Constant.RAG_CONFIG_TYPE)
|
.select("id", "model_name")
|
||||||
|
.eq("model_type", Constant.RAG_CONFIG_TYPE)
|
||||||
.eq("is_enabled", 1)
|
.eq("is_enabled", 1)
|
||||||
.orderByDesc("is_default")
|
.orderByDesc("is_default")
|
||||||
.orderByDesc("create_date");
|
.orderByDesc("create_date");
|
||||||
|
|
||||||
List<ModelConfigEntity> modelConfigs = modelConfigDao.selectList(queryWrapper);
|
List<ModelConfigEntity> modelConfigs = modelConfigDao.selectList(queryWrapper);
|
||||||
|
return modelConfigs;
|
||||||
List<Map<String, Object>> modelList = new ArrayList<>();
|
|
||||||
for (ModelConfigEntity modelConfig : modelConfigs) {
|
|
||||||
Map<String, Object> modelInfo = new HashMap<>();
|
|
||||||
modelInfo.put("id", modelConfig.getId());
|
|
||||||
modelInfo.put("modelName", modelConfig.getModelName());
|
|
||||||
modelList.add(modelInfo);
|
|
||||||
}
|
|
||||||
return modelList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+9
-80
@@ -3,24 +3,10 @@ package xiaozhi.modules.knowledge.service.impl;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.core.io.AbstractResource;
|
import org.springframework.core.io.AbstractResource;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@@ -31,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import xiaozhi.common.exception.ErrorCode;
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
|
import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO;
|
||||||
import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter;
|
import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter;
|
||||||
import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory;
|
import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory;
|
||||||
@@ -43,8 +30,6 @@ import xiaozhi.modules.knowledge.service.KnowledgeFilesService;
|
|||||||
public class KnowledgeFilesServiceImpl implements KnowledgeFilesService {
|
public class KnowledgeFilesServiceImpl implements KnowledgeFilesService {
|
||||||
|
|
||||||
private final KnowledgeBaseService knowledgeBaseService;
|
private final KnowledgeBaseService knowledgeBaseService;
|
||||||
private RestTemplate restTemplate = new RestTemplate();
|
|
||||||
private ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getRAGConfig(String ragModelId) {
|
public Map<String, Object> getRAGConfig(String ragModelId) {
|
||||||
@@ -55,32 +40,29 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService {
|
|||||||
public PageData<KnowledgeFilesDTO> getPageList(KnowledgeFilesDTO knowledgeFilesDTO, Integer page, Integer limit) {
|
public PageData<KnowledgeFilesDTO> getPageList(KnowledgeFilesDTO knowledgeFilesDTO, Integer page, Integer limit) {
|
||||||
try {
|
try {
|
||||||
log.info("=== 开始获取文档列表 ===");
|
log.info("=== 开始获取文档列表 ===");
|
||||||
log.info("查询条件: datasetId={}, name={}, status={}, page={}, limit={}",
|
log.info("查询条件: datasetId={}, name={}, status={}, page={}, limit={}", knowledgeFilesDTO.getDatasetId(), knowledgeFilesDTO.getName(), knowledgeFilesDTO.getStatus(), page, limit);
|
||||||
knowledgeFilesDTO != null ? knowledgeFilesDTO.getDatasetId() : null,
|
|
||||||
knowledgeFilesDTO != null ? knowledgeFilesDTO.getName() : null,
|
|
||||||
knowledgeFilesDTO != null ? knowledgeFilesDTO.getStatus() : null,
|
|
||||||
page, limit);
|
|
||||||
|
|
||||||
// 获取数据集ID
|
// 获取数据集ID
|
||||||
String datasetId = knowledgeFilesDTO != null ? knowledgeFilesDTO.getDatasetId() : null;
|
String datasetId = knowledgeFilesDTO.getDatasetId();
|
||||||
if (StringUtils.isBlank(datasetId)) {
|
if (ToolUtil.isEmpty(datasetId)) {
|
||||||
throw new RenException(ErrorCode.RAG_DATASET_ID_NOT_NULL);
|
throw new RenException(ErrorCode.RAG_DATASET_ID_NOT_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取RAG配置
|
// 获取RAG配置
|
||||||
Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
|
Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
|
||||||
|
|
||||||
// 提取适配器类型
|
// 提取适配器类型
|
||||||
String adapterType = extractAdapterType(ragConfig);
|
String adapterType = extractAdapterType(ragConfig);
|
||||||
|
|
||||||
// 使用适配器工厂获取适配器实例
|
// 使用适配器工厂获取适配器实例
|
||||||
KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig);
|
KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig);
|
||||||
|
|
||||||
// 构建查询参数
|
// 构建查询参数
|
||||||
Map<String, Object> queryParams = new HashMap<>();
|
Map<String, Object> queryParams = new HashMap<>();
|
||||||
if (knowledgeFilesDTO != null && StringUtils.isNotBlank(knowledgeFilesDTO.getName())) {
|
if (ToolUtil.isNotEmpty(knowledgeFilesDTO.getName())) {
|
||||||
queryParams.put("keywords", knowledgeFilesDTO.getName());
|
queryParams.put("keywords", knowledgeFilesDTO.getName());
|
||||||
}
|
}
|
||||||
|
if (ToolUtil.isNotEmpty(knowledgeFilesDTO.getStatus())) {
|
||||||
|
queryParams.put("status", knowledgeFilesDTO.getStatus());
|
||||||
|
}
|
||||||
if (page > 0) {
|
if (page > 0) {
|
||||||
queryParams.put("page", page);
|
queryParams.put("page", page);
|
||||||
}
|
}
|
||||||
@@ -90,10 +72,8 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService {
|
|||||||
|
|
||||||
// 调用适配器获取文档列表
|
// 调用适配器获取文档列表
|
||||||
PageData<KnowledgeFilesDTO> result = adapter.getDocumentList(datasetId, queryParams, page, limit);
|
PageData<KnowledgeFilesDTO> result = adapter.getDocumentList(datasetId, queryParams, page, limit);
|
||||||
|
|
||||||
log.info("获取文档列表成功,共{}个文档,总数: {}", result.getList().size(), result.getTotal());
|
log.info("获取文档列表成功,共{}个文档,总数: {}", result.getList().size(), result.getTotal());
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取文档列表失败: {}", e.getMessage(), e);
|
log.error("获取文档列表失败: {}", e.getMessage(), e);
|
||||||
if (e instanceof RenException) {
|
if (e instanceof RenException) {
|
||||||
@@ -424,57 +404,6 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageData<KnowledgeFilesDTO> getPageListByStatus(String datasetId, Integer status, Integer page,
|
|
||||||
Integer limit) {
|
|
||||||
if (StringUtils.isBlank(datasetId)) {
|
|
||||||
throw new RenException(ErrorCode.RAG_DATASET_ID_NOT_NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("=== 开始根据状态查询文档列表 ===");
|
|
||||||
log.info("datasetId: {}, status: {}, page: {}, limit: {}", datasetId, status, page, limit);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 获取RAG配置
|
|
||||||
Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
|
|
||||||
|
|
||||||
// 提取适配器类型
|
|
||||||
String adapterType = extractAdapterType(ragConfig);
|
|
||||||
|
|
||||||
// 使用适配器工厂获取适配器实例
|
|
||||||
KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig);
|
|
||||||
|
|
||||||
// 构建查询参数
|
|
||||||
Map<String, Object> queryParams = new HashMap<>();
|
|
||||||
if (page != null && page > 0) {
|
|
||||||
queryParams.put("page", page);
|
|
||||||
}
|
|
||||||
if (limit != null && limit > 0) {
|
|
||||||
queryParams.put("page_size", limit);
|
|
||||||
}
|
|
||||||
if (status != null) {
|
|
||||||
queryParams.put("status", status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用适配器获取文档列表
|
|
||||||
PageData<KnowledgeFilesDTO> pageData = adapter.getDocumentList(datasetId, queryParams, page, limit);
|
|
||||||
|
|
||||||
if (pageData != null) {
|
|
||||||
log.info("根据状态查询文档列表成功,datasetId: {}, 状态: {}, 文档数量: {}",
|
|
||||||
datasetId, status, pageData.getList().size());
|
|
||||||
return pageData;
|
|
||||||
} else {
|
|
||||||
throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("根据状态查询文档列表失败: {}", e.getMessage(), e);
|
|
||||||
throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
log.info("=== 根据状态查询文档列表操作结束 ===");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KnowledgeFilesDTO uploadDocument(String datasetId, MultipartFile file, String name,
|
public KnowledgeFilesDTO uploadDocument(String datasetId, MultipartFile file, String name,
|
||||||
Map<String, Object> metaFields, String chunkMethod,
|
Map<String, Object> metaFields, String chunkMethod,
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package xiaozhi.modules.llm.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLM服务接口
|
||||||
|
* 支持多种大模型调用
|
||||||
|
*/
|
||||||
|
public interface LLMService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成聊天记录总结
|
||||||
|
*
|
||||||
|
* @param conversation 对话内容
|
||||||
|
* @param promptTemplate 提示词模板
|
||||||
|
* @return 总结结果
|
||||||
|
*/
|
||||||
|
String generateSummary(String conversation, String promptTemplate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成聊天记录总结(使用默认提示词)
|
||||||
|
*
|
||||||
|
* @param conversation 对话内容
|
||||||
|
* @return 总结结果
|
||||||
|
*/
|
||||||
|
String generateSummary(String conversation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成聊天记录总结(指定模型ID)
|
||||||
|
*
|
||||||
|
* @param conversation 对话内容
|
||||||
|
* @param modelId 模型ID
|
||||||
|
* @return 总结结果
|
||||||
|
*/
|
||||||
|
String generateSummaryWithModel(String conversation, String modelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成聊天记录总结(指定模型ID和提示词模板)
|
||||||
|
*
|
||||||
|
* @param conversation 对话内容
|
||||||
|
* @param promptTemplate 提示词模板
|
||||||
|
* @param modelId 模型ID
|
||||||
|
* @return 总结结果
|
||||||
|
*/
|
||||||
|
String generateSummary(String conversation, String promptTemplate, String modelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成聊天记录总结(包含历史记忆合并)
|
||||||
|
*
|
||||||
|
* @param conversation 对话内容
|
||||||
|
* @param historyMemory 历史记忆
|
||||||
|
* @param promptTemplate 提示词模板
|
||||||
|
* @param modelId 模型ID
|
||||||
|
* @return 总结结果
|
||||||
|
*/
|
||||||
|
String generateSummaryWithHistory(String conversation, String historyMemory, String promptTemplate, String modelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查服务是否可用
|
||||||
|
*
|
||||||
|
* @return 是否可用
|
||||||
|
*/
|
||||||
|
boolean isAvailable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查指定模型的服务是否可用
|
||||||
|
*
|
||||||
|
* @param modelId 模型ID
|
||||||
|
* @return 是否可用
|
||||||
|
*/
|
||||||
|
boolean isAvailable(String modelId);
|
||||||
|
}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
package xiaozhi.modules.llm.service.impl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import xiaozhi.modules.llm.service.LLMService;
|
||||||
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI风格API的LLM服务实现
|
||||||
|
* 支持阿里云、DeepSeek、ChatGLM等兼容OpenAI API的模型
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class OpenAIStyleLLMServiceImpl implements LLMService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ModelConfigService modelConfigService;
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
private static final String DEFAULT_SUMMARY_PROMPT = "你是一个经验丰富的记忆总结者,擅长将对话内容进行总结摘要,遵循以下规则:\n1、总结用户的重要信息,以便在未来的对话中提供更个性化的服务\n2、不要重复总结,不要遗忘之前记忆,除非原来的记忆超过了1800字,否则不要遗忘、不要压缩用户的历史记忆\n3、用户操控的设备音量、播放音乐、天气、退出、不想对话等和用户本身无关的内容,这些信息不需要加入到总结中\n4、聊天内容中的今天的日期时间、今天的天气情况与用户事件无关的数据,这些信息如果当成记忆存储会影响后续对话,这些信息不需要加入到总结中\n5、不要把设备操控的成果结果和失败结果加入到总结中,也不要把用户的一些废话加入到总结中\n6、不要为了总结而总结,如果用户的聊天没有意义,请返回原来的历史记录也是可以的\n7、只需要返回总结摘要,严格控制在1800字内\n8、不要包含代码、xml,不需要解释、注释和说明,保存记忆时仅从对话提取信息,不要混入示例内容\n9、如果提供了历史记忆,请将新对话内容与历史记忆进行智能合并,保留有价值的历史信息,同时添加新的重要信息\n\n历史记忆:\n{history_memory}\n\n新对话内容:\n{conversation}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateSummary(String conversation) {
|
||||||
|
return generateSummary(conversation, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateSummaryWithModel(String conversation, String modelId) {
|
||||||
|
return generateSummary(conversation, null, modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateSummary(String conversation, String promptTemplate, String modelId) {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
log.warn("LLM服务不可用,无法生成总结");
|
||||||
|
return "LLM服务不可用,无法生成总结";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从智控台获取LLM模型配置
|
||||||
|
ModelConfigEntity llmConfig;
|
||||||
|
if (modelId != null && !modelId.trim().isEmpty()) {
|
||||||
|
// 通过具体模型ID获取配置
|
||||||
|
llmConfig = modelConfigService.getModelByIdFromCache(modelId);
|
||||||
|
} else {
|
||||||
|
// 保持向后兼容,使用默认配置
|
||||||
|
llmConfig = getDefaultLLMConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llmConfig == null || llmConfig.getConfigJson() == null) {
|
||||||
|
log.error("未找到可用的LLM模型配置,modelId: {}", modelId);
|
||||||
|
return "未找到可用的LLM模型配置";
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject configJson = llmConfig.getConfigJson();
|
||||||
|
String baseUrl = configJson.getStr("base_url");
|
||||||
|
String model = configJson.getStr("model_name");
|
||||||
|
String apiKey = configJson.getStr("api_key");
|
||||||
|
Double temperature = configJson.getDouble("temperature");
|
||||||
|
Integer maxTokens = configJson.getInt("max_tokens");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
|
||||||
|
log.error("LLM配置不完整,baseUrl或apiKey为空");
|
||||||
|
return "LLM配置不完整,无法生成总结";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建提示词
|
||||||
|
String prompt = (promptTemplate != null ? promptTemplate : DEFAULT_SUMMARY_PROMPT).replace("{conversation}",
|
||||||
|
conversation);
|
||||||
|
|
||||||
|
// 构建请求体
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
||||||
|
|
||||||
|
Map<String, Object>[] messages = new Map[1];
|
||||||
|
Map<String, Object> message = new HashMap<>();
|
||||||
|
message.put("role", "user");
|
||||||
|
message.put("content", prompt);
|
||||||
|
messages[0] = message;
|
||||||
|
|
||||||
|
requestBody.put("messages", messages);
|
||||||
|
requestBody.put("temperature", temperature != null ? temperature : 0.7);
|
||||||
|
requestBody.put("max_tokens", maxTokens != null ? maxTokens : 2000);
|
||||||
|
|
||||||
|
// 发送HTTP请求
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.set("Authorization", "Bearer " + apiKey);
|
||||||
|
|
||||||
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
|
||||||
|
|
||||||
|
// 构建完整的API URL
|
||||||
|
String apiUrl = baseUrl;
|
||||||
|
if (!apiUrl.endsWith("/chat/completions")) {
|
||||||
|
if (!apiUrl.endsWith("/")) {
|
||||||
|
apiUrl += "/";
|
||||||
|
}
|
||||||
|
apiUrl += "chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
apiUrl, HttpMethod.POST, entity, String.class);
|
||||||
|
|
||||||
|
if (response.getStatusCode().is2xxSuccessful()) {
|
||||||
|
JSONObject responseJson = JSONUtil.parseObj(response.getBody());
|
||||||
|
JSONArray choices = responseJson.getJSONArray("choices");
|
||||||
|
if (choices != null && choices.size() > 0) {
|
||||||
|
JSONObject choice = choices.getJSONObject(0);
|
||||||
|
JSONObject messageObj = choice.getJSONObject("message");
|
||||||
|
return messageObj.getStr("content");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("LLM API调用失败,状态码:{},响应:{}", response.getStatusCode(), response.getBody());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用LLM服务生成总结时发生异常,modelId: {}", modelId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "生成总结失败,请稍后重试";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateSummary(String conversation, String promptTemplate) {
|
||||||
|
return generateSummary(conversation, promptTemplate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateSummaryWithHistory(String conversation, String historyMemory, String promptTemplate,
|
||||||
|
String modelId) {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
log.warn("LLM服务不可用,无法生成总结");
|
||||||
|
return "LLM服务不可用,无法生成总结";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从智控台获取LLM模型配置
|
||||||
|
ModelConfigEntity llmConfig;
|
||||||
|
if (modelId != null && !modelId.trim().isEmpty()) {
|
||||||
|
// 通过具体模型ID获取配置
|
||||||
|
llmConfig = modelConfigService.getModelByIdFromCache(modelId);
|
||||||
|
} else {
|
||||||
|
// 保持向后兼容,使用默认配置
|
||||||
|
llmConfig = getDefaultLLMConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llmConfig == null || llmConfig.getConfigJson() == null) {
|
||||||
|
log.error("未找到可用的LLM模型配置,modelId: {}", modelId);
|
||||||
|
return "未找到可用的LLM模型配置";
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject configJson = llmConfig.getConfigJson();
|
||||||
|
String baseUrl = configJson.getStr("base_url");
|
||||||
|
String model = configJson.getStr("model_name");
|
||||||
|
String apiKey = configJson.getStr("api_key");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
|
||||||
|
log.error("LLM配置不完整,baseUrl或apiKey为空");
|
||||||
|
return "LLM配置不完整,无法生成总结";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建提示词,包含历史记忆
|
||||||
|
String prompt = (promptTemplate != null ? promptTemplate : DEFAULT_SUMMARY_PROMPT)
|
||||||
|
.replace("{history_memory}", historyMemory != null ? historyMemory : "无历史记忆")
|
||||||
|
.replace("{conversation}", conversation);
|
||||||
|
|
||||||
|
// 构建请求体
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
||||||
|
|
||||||
|
Map<String, Object>[] messages = new Map[1];
|
||||||
|
Map<String, Object> message = new HashMap<>();
|
||||||
|
message.put("role", "user");
|
||||||
|
message.put("content", prompt);
|
||||||
|
messages[0] = message;
|
||||||
|
|
||||||
|
requestBody.put("messages", messages);
|
||||||
|
requestBody.put("temperature", 0.2);
|
||||||
|
requestBody.put("max_tokens", 2000);
|
||||||
|
|
||||||
|
// 发送HTTP请求
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.set("Authorization", "Bearer " + apiKey);
|
||||||
|
|
||||||
|
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
|
||||||
|
|
||||||
|
// 构建完整的API URL
|
||||||
|
String apiUrl = baseUrl;
|
||||||
|
if (!apiUrl.endsWith("/chat/completions")) {
|
||||||
|
if (!apiUrl.endsWith("/")) {
|
||||||
|
apiUrl += "/";
|
||||||
|
}
|
||||||
|
apiUrl += "chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
apiUrl, HttpMethod.POST, entity, String.class);
|
||||||
|
|
||||||
|
if (response.getStatusCode().is2xxSuccessful()) {
|
||||||
|
JSONObject responseJson = JSONUtil.parseObj(response.getBody());
|
||||||
|
JSONArray choices = responseJson.getJSONArray("choices");
|
||||||
|
if (choices != null && choices.size() > 0) {
|
||||||
|
JSONObject choice = choices.getJSONObject(0);
|
||||||
|
JSONObject messageObj = choice.getJSONObject("message");
|
||||||
|
return messageObj.getStr("content");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("LLM API调用失败,状态码:{},响应:{}", response.getStatusCode(), response.getBody());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用LLM服务生成总结时发生异常,modelId: {}", modelId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "生成总结失败,请稍后重试";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
try {
|
||||||
|
ModelConfigEntity defaultLLMConfig = getDefaultLLMConfig();
|
||||||
|
if (defaultLLMConfig == null || defaultLLMConfig.getConfigJson() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject configJson = defaultLLMConfig.getConfigJson();
|
||||||
|
String baseUrl = configJson.getStr("base_url");
|
||||||
|
String apiKey = configJson.getStr("api_key");
|
||||||
|
|
||||||
|
return baseUrl != null && !baseUrl.trim().isEmpty() &&
|
||||||
|
apiKey != null && !apiKey.trim().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("检查LLM服务可用性时发生异常:", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable(String modelId) {
|
||||||
|
try {
|
||||||
|
if (modelId == null || modelId.trim().isEmpty()) {
|
||||||
|
return isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过具体模型ID获取配置
|
||||||
|
ModelConfigEntity modelConfig = modelConfigService.getModelByIdFromCache(modelId);
|
||||||
|
if (modelConfig == null || modelConfig.getConfigJson() == null) {
|
||||||
|
log.warn("未找到指定的LLM模型配置,modelId: {}", modelId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject configJson = modelConfig.getConfigJson();
|
||||||
|
String baseUrl = configJson.getStr("base_url");
|
||||||
|
String apiKey = configJson.getStr("api_key");
|
||||||
|
|
||||||
|
return baseUrl != null && !baseUrl.trim().isEmpty() &&
|
||||||
|
apiKey != null && !apiKey.trim().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("检查LLM服务可用性时发生异常,modelId: {}", modelId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从智控台获取默认的LLM模型配置
|
||||||
|
*/
|
||||||
|
private ModelConfigEntity getDefaultLLMConfig() {
|
||||||
|
try {
|
||||||
|
// 获取所有启用的LLM模型配置
|
||||||
|
List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
|
||||||
|
if (llmConfigs == null || llmConfigs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先返回默认配置,如果没有默认配置则返回第一个启用的配置
|
||||||
|
for (ModelConfigEntity config : llmConfigs) {
|
||||||
|
if (config.getIsDefault() != null && config.getIsDefault() == 1) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return llmConfigs.get(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取LLM模型配置时发生异常:", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,24 @@ public class VoiceDTO implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "音频播放地址")
|
@Schema(description = "音频播放地址")
|
||||||
private String voiceDemo;
|
private String voiceDemo;
|
||||||
|
|
||||||
|
@Schema(description = "是否为克隆音色")
|
||||||
|
private Boolean isClone;
|
||||||
|
|
||||||
// 添加双参数构造函数,保持向后兼容
|
// 添加双参数构造函数,保持向后兼容
|
||||||
public VoiceDTO(String id, String name) {
|
public VoiceDTO(String id, String name) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.voiceDemo = null;
|
this.voiceDemo = null;
|
||||||
|
this.isClone = false; // 默认不是克隆音色
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加三参数构造函数,用于普通音色
|
||||||
|
public VoiceDTO(String id, String name, String voiceDemo) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.voiceDemo = voiceDemo;
|
||||||
|
this.isClone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -55,4 +55,12 @@ public interface ModelConfigService extends BaseService<ModelConfigEntity> {
|
|||||||
* @return TTS平台列表(id和modelName)
|
* @return TTS平台列表(id和modelName)
|
||||||
*/
|
*/
|
||||||
List<Map<String, Object>> getTtsPlatformList();
|
List<Map<String, Object>> getTtsPlatformList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据模型类型获取所有启用的模型配置
|
||||||
|
*
|
||||||
|
* @param modelType 模型类型(如:LLM, TTS, ASR等)
|
||||||
|
* @return 启用的模型配置列表
|
||||||
|
*/
|
||||||
|
List<ModelConfigEntity> getEnabledModelsByType(String modelType);
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-7
@@ -56,7 +56,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", modelType)
|
.eq("model_type", modelType)
|
||||||
.eq("is_enabled", 1)
|
.eq("is_enabled", 1)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||||
.select("id", "model_name"));
|
.select("id", "model_name"));
|
||||||
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
|
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
|
||||||
}
|
}
|
||||||
@@ -67,14 +67,14 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", "llm")
|
.eq("model_type", "llm")
|
||||||
.eq("is_enabled", 1)
|
.eq("is_enabled", 1)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||||
.select("id", "model_name", "config_json"));
|
.select("id", "model_name", "config_json"));
|
||||||
|
|
||||||
return entities.stream().map(item -> {
|
return entities.stream().map(item -> {
|
||||||
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
|
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
|
||||||
dto.setId(item.getId());
|
dto.setId(item.getId());
|
||||||
dto.setModelName(item.getModelName());
|
dto.setModelName(item.getModelName());
|
||||||
String type = item.getConfigJson().get("type").toString();
|
String type = item.getConfigJson().getOrDefault("type", "").toString();
|
||||||
dto.setType(type);
|
dto.setType(type);
|
||||||
return dto;
|
return dto;
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -91,14 +91,13 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
|
Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
|
||||||
|
|
||||||
// 添加排序规则:先按is_enabled降序,再按sort升序
|
// 添加排序规则:先按is_enabled降序,再按sort升序
|
||||||
pageInfo.addOrder(OrderItem.desc("is_enabled"));
|
pageInfo.addOrder(OrderItem.desc("is_enabled"), OrderItem.asc("sort"));
|
||||||
pageInfo.addOrder(OrderItem.asc("sort"));
|
|
||||||
|
|
||||||
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
||||||
pageInfo,
|
pageInfo,
|
||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", modelType)
|
.eq("model_type", modelType)
|
||||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%"));
|
.like(StringUtils.isNotBlank(modelName), "model_name", modelName));
|
||||||
|
|
||||||
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
||||||
}
|
}
|
||||||
@@ -346,6 +345,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
modelConfigEntity.setModelName(modelConfigBodyDTO.getModelName());
|
modelConfigEntity.setModelName(modelConfigBodyDTO.getModelName());
|
||||||
modelConfigEntity.setSort(modelConfigBodyDTO.getSort());
|
modelConfigEntity.setSort(modelConfigBodyDTO.getSort());
|
||||||
modelConfigEntity.setIsEnabled(modelConfigBodyDTO.getIsEnabled());
|
modelConfigEntity.setIsEnabled(modelConfigBodyDTO.getIsEnabled());
|
||||||
|
modelConfigEntity.setRemark(modelConfigBodyDTO.getRemark());
|
||||||
// 3. 处理配置JSON,仅更新非敏感字段和明确修改的敏感字段
|
// 3. 处理配置JSON,仅更新非敏感字段和明确修改的敏感字段
|
||||||
if (modelConfigBodyDTO.getConfigJson() != null && originalEntity.getConfigJson() != null) {
|
if (modelConfigBodyDTO.getConfigJson() != null && originalEntity.getConfigJson() != null) {
|
||||||
JSONObject originalJson = originalEntity.getConfigJson();
|
JSONObject originalJson = originalEntity.getConfigJson();
|
||||||
@@ -488,7 +488,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
List<ModelConfigEntity> intentConfigs = modelConfigDao.selectList(
|
List<ModelConfigEntity> intentConfigs = modelConfigDao.selectList(
|
||||||
new QueryWrapper<ModelConfigEntity>()
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
.eq("model_type", "Intent")
|
.eq("model_type", "Intent")
|
||||||
.like("config_json", "%" + modelId + "%"));
|
.like("config_json", modelId));
|
||||||
if (!intentConfigs.isEmpty()) {
|
if (!intentConfigs.isEmpty()) {
|
||||||
throw new RenException(ErrorCode.LLM_REFERENCED_BY_INTENT);
|
throw new RenException(ErrorCode.LLM_REFERENCED_BY_INTENT);
|
||||||
}
|
}
|
||||||
@@ -502,4 +502,22 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
public List<Map<String, Object>> getTtsPlatformList() {
|
public List<Map<String, Object>> getTtsPlatformList() {
|
||||||
return modelConfigDao.getTtsPlatformList();
|
return modelConfigDao.getTtsPlatformList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据模型类型获取所有启用的模型配置
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<ModelConfigEntity> getEnabledModelsByType(String modelType) {
|
||||||
|
if (StringUtils.isBlank(modelType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ModelConfigEntity> entities = modelConfigDao.selectList(
|
||||||
|
new QueryWrapper<ModelConfigEntity>()
|
||||||
|
.eq("model_type", modelType)
|
||||||
|
.eq("is_enabled", 1)
|
||||||
|
.orderByAsc("sort"));
|
||||||
|
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-8
@@ -124,12 +124,6 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
|||||||
return getPageData(modelProviderDao.selectPage(pageParam, wrapper), ModelProviderDTO.class);
|
return getPageData(modelProviderDao.selectPage(pageParam, wrapper), ModelProviderDTO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
String jsonString = "\"[]\"";
|
|
||||||
JSONArray jsonArray = new JSONArray(jsonString);
|
|
||||||
System.out.println("字符串转 JSONArray: " + jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModelProviderDTO add(ModelProviderDTO modelProviderDTO) {
|
public ModelProviderDTO add(ModelProviderDTO modelProviderDTO) {
|
||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
@@ -153,8 +147,7 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
|||||||
UserDetail user = SecurityUser.getUser();
|
UserDetail user = SecurityUser.getUser();
|
||||||
modelProviderDTO.setUpdater(user.getId());
|
modelProviderDTO.setUpdater(user.getId());
|
||||||
modelProviderDTO.setUpdateDate(new Date());
|
modelProviderDTO.setUpdateDate(new Date());
|
||||||
if (modelProviderDao
|
if (modelProviderDao.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
|
||||||
.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
|
|
||||||
throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
|
throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
|
||||||
}
|
}
|
||||||
return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
|
return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class ShiroConfig {
|
|||||||
filterMap.put("/config/**", "server");
|
filterMap.put("/config/**", "server");
|
||||||
filterMap.put("/agent/chat-history/report", "server");
|
filterMap.put("/agent/chat-history/report", "server");
|
||||||
filterMap.put("/agent/chat-history/download/**", "anon");
|
filterMap.put("/agent/chat-history/download/**", "anon");
|
||||||
filterMap.put("/agent/saveMemory/**", "server");
|
filterMap.put("/agent/chat-summary/**", "server");
|
||||||
filterMap.put("/agent/play/**", "anon");
|
filterMap.put("/agent/play/**", "anon");
|
||||||
filterMap.put("/voiceClone/play/**", "anon");
|
filterMap.put("/voiceClone/play/**", "anon");
|
||||||
filterMap.put("/**", "oauth2");
|
filterMap.put("/**", "oauth2");
|
||||||
|
|||||||
+19
-2
@@ -31,6 +31,8 @@ import xiaozhi.modules.sys.dto.ServerActionResponseDTO;
|
|||||||
import xiaozhi.modules.sys.enums.ServerActionEnum;
|
import xiaozhi.modules.sys.enums.ServerActionEnum;
|
||||||
import xiaozhi.modules.sys.service.SysParamsService;
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
import xiaozhi.modules.sys.utils.WebSocketClientManager;
|
import xiaozhi.modules.sys.utils.WebSocketClientManager;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务端管理控制器
|
* 服务端管理控制器
|
||||||
@@ -41,6 +43,8 @@ import xiaozhi.modules.sys.utils.WebSocketClientManager;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ServerSideManageController {
|
public class ServerSideManageController {
|
||||||
private final SysParamsService sysParamsService;
|
private final SysParamsService sysParamsService;
|
||||||
|
private final DeviceService deviceService;
|
||||||
|
private final RedisUtils redisUtils;
|
||||||
private static final ObjectMapper objectMapper;
|
private static final ObjectMapper objectMapper;
|
||||||
static {
|
static {
|
||||||
objectMapper = new ObjectMapper();
|
objectMapper = new ObjectMapper();
|
||||||
@@ -85,9 +89,22 @@ public class ServerSideManageController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String serverSK = sysParamsService.getValue(Constant.SERVER_SECRET, true);
|
String serverSK = sysParamsService.getValue(Constant.SERVER_SECRET, true);
|
||||||
|
|
||||||
|
String deviceId = UUID.randomUUID().toString();
|
||||||
|
String clientId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
String redisKey = xiaozhi.common.redis.RedisKeys.getTmpRegisterMacKey(deviceId);
|
||||||
|
redisUtils.set(redisKey, "true", 300); // 5分钟有效期
|
||||||
|
|
||||||
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
|
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
|
||||||
headers.add("device-id", UUID.randomUUID().toString());
|
headers.add("device-id", deviceId);
|
||||||
headers.add("client-id", UUID.randomUUID().toString());
|
headers.add("client-id", clientId);
|
||||||
|
try {
|
||||||
|
String token = deviceService.generateWebSocketToken(clientId, deviceId);
|
||||||
|
headers.add("authorization", "Bearer " + token);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RenException(ErrorCode.WEB_SOCKET_CONNECT_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
try (WebSocketClientManager client = new WebSocketClientManager.Builder()
|
try (WebSocketClientManager client = new WebSocketClientManager.Builder()
|
||||||
.connectTimeout(3, TimeUnit.SECONDS)
|
.connectTimeout(3, TimeUnit.SECONDS)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
import xiaozhi.common.dao.BaseDao;
|
import xiaozhi.common.dao.BaseDao;
|
||||||
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
||||||
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
@@ -23,4 +24,9 @@ public interface SysDictDataDao extends BaseDao<SysDictDataEntity> {
|
|||||||
* @return 字典类型编码
|
* @return 字典类型编码
|
||||||
*/
|
*/
|
||||||
String getTypeByTypeId(Long dictTypeId);
|
String getTypeByTypeId(Long dictTypeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典数据ID集合获取字典类型编码集合
|
||||||
|
*/
|
||||||
|
List<String> getDictTypesByIdList(@Param("dictDataIdList") List<Long> dictDataIdList);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-11
@@ -1,9 +1,6 @@
|
|||||||
package xiaozhi.modules.sys.service.impl;
|
package xiaozhi.modules.sys.service.impl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -23,6 +20,7 @@ import xiaozhi.common.redis.RedisKeys;
|
|||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
||||||
import xiaozhi.modules.sys.dao.SysUserDao;
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||||
@@ -104,13 +102,19 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void delete(Long[] ids) {
|
public void delete(Long[] ids) {
|
||||||
for (Long id : ids) {
|
List<Long> idList = Arrays.asList(ids);
|
||||||
SysDictDataEntity entity = baseDao.selectById(id);
|
if (ToolUtil.isNotEmpty(idList)) {
|
||||||
// 删除Redis缓存
|
//批量删除redis字典
|
||||||
String dictType = baseDao.getTypeByTypeId(entity.getDictTypeId());
|
List<String> redisKeyList = new ArrayList<>();
|
||||||
redisUtils.delete(RedisKeys.getDictDataByTypeKey(dictType));
|
//批量获取字典类型
|
||||||
// 删除
|
List<String> dictTypeList = Optional.ofNullable(baseDao.getDictTypesByIdList(idList)).orElseGet(ArrayList::new);
|
||||||
deleteById(id);
|
dictTypeList.forEach(dictType -> redisKeyList.add(RedisKeys.getDictDataByTypeKey(dictType)));
|
||||||
|
if (ToolUtil.isNotEmpty(redisKeyList)) {
|
||||||
|
//清除缓存
|
||||||
|
redisUtils.delete(redisKeyList);
|
||||||
|
}
|
||||||
|
//批量删除字典数据
|
||||||
|
deleteBatchIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -1,10 +1,6 @@
|
|||||||
package xiaozhi.modules.timbre.service.impl;
|
package xiaozhi.modules.timbre.service.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -128,14 +124,12 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
if (StringUtils.isNotBlank(voiceName)) {
|
if (StringUtils.isNotBlank(voiceName)) {
|
||||||
queryWrapper.like("name", voiceName);
|
queryWrapper.like("name", voiceName);
|
||||||
}
|
}
|
||||||
List<TimbreEntity> timbreEntities = timbreDao.selectList(queryWrapper);
|
List<TimbreEntity> timbreEntities = Optional.ofNullable(timbreDao.selectList(queryWrapper)).orElseGet(ArrayList::new);
|
||||||
if (timbreEntities == null) {
|
|
||||||
timbreEntities = new ArrayList<>();
|
|
||||||
}
|
|
||||||
List<VoiceDTO> voiceDTOs = timbreEntities.stream()
|
List<VoiceDTO> voiceDTOs = timbreEntities.stream()
|
||||||
.map(entity -> {
|
.map(entity -> {
|
||||||
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
||||||
dto.setVoiceDemo(entity.getVoiceDemo());
|
dto.setVoiceDemo(entity.getVoiceDemo());
|
||||||
|
dto.setIsClone(false); // 设置为普通音色
|
||||||
return dto;
|
return dto;
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -152,6 +146,7 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
|
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
|
||||||
// 保留从数据库查询到的voiceDemo字段
|
// 保留从数据库查询到的voiceDemo字段
|
||||||
voiceDTO.setVoiceDemo(entity.getVoiceDemo());
|
voiceDTO.setVoiceDemo(entity.getVoiceDemo());
|
||||||
|
voiceDTO.setIsClone(true); // 设置为克隆音色
|
||||||
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
|
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
|
||||||
RedisUtils.NOT_EXPIRE);
|
RedisUtils.NOT_EXPIRE);
|
||||||
voiceDTOs.add(0, voiceDTO);
|
voiceDTOs.add(0, voiceDTO);
|
||||||
@@ -214,6 +209,7 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
TimbreEntity entity = list.get(0);
|
TimbreEntity entity = list.get(0);
|
||||||
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
|
||||||
dto.setVoiceDemo(entity.getVoiceDemo());
|
dto.setVoiceDemo(entity.getVoiceDemo());
|
||||||
|
dto.setIsClone(false); // 设置为普通音色
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
@@ -83,6 +83,13 @@ public class VoiceCloneController {
|
|||||||
return new Result<String>().error(ErrorCode.VOICE_CLONE_NOT_AUDIO_FILE);
|
return new Result<String>().error(ErrorCode.VOICE_CLONE_NOT_AUDIO_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加强验证文件扩展名
|
||||||
|
String originalFilename = voiceFile.getOriginalFilename();
|
||||||
|
String extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
|
||||||
|
if (!extension.equals(".mp3") && !extension.equals(".wav")) {
|
||||||
|
return new Result<String>().error("只允许上传.mp3和.wav格式的文件");
|
||||||
|
}
|
||||||
|
|
||||||
// 验证文件大小 (最大10MB)
|
// 验证文件大小 (最大10MB)
|
||||||
if (voiceFile.getSize() > 10 * 1024 * 1024) {
|
if (voiceFile.getSize() > 10 * 1024 * 1024) {
|
||||||
return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
|
return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
|
||||||
|
|||||||
+19
-10
@@ -4,12 +4,8 @@ import java.net.URI;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -30,8 +26,11 @@ import xiaozhi.common.page.PageData;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
|
import xiaozhi.common.utils.ToolUtil;
|
||||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
|
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||||
import xiaozhi.modules.sys.service.SysUserService;
|
import xiaozhi.modules.sys.service.SysUserService;
|
||||||
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
|
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
|
||||||
import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
|
import xiaozhi.modules.voiceclone.dto.VoiceCloneDTO;
|
||||||
@@ -47,6 +46,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
private final ModelConfigService modelConfigService;
|
private final ModelConfigService modelConfigService;
|
||||||
private final SysUserService sysUserService;
|
private final SysUserService sysUserService;
|
||||||
|
private final SysUserDao sysUserDao;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,9 +104,11 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量保存
|
||||||
|
List<VoiceCloneEntity> batchInsertList = new ArrayList<>();
|
||||||
// 遍历选择的音色ID,为每个音色ID创建一条记录
|
// 遍历选择的音色ID,为每个音色ID创建一条记录
|
||||||
int index = 0;
|
int index = 0;
|
||||||
String namePrefix = DateUtils.format(new java.util.Date(), "MMddHHmm");
|
String namePrefix = DateUtils.format(new Date(), "MMddHHmm");
|
||||||
for (String voiceId : dto.getVoiceIds()) {
|
for (String voiceId : dto.getVoiceIds()) {
|
||||||
index++;
|
index++;
|
||||||
VoiceCloneEntity entity = new VoiceCloneEntity();
|
VoiceCloneEntity entity = new VoiceCloneEntity();
|
||||||
@@ -115,8 +117,10 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
entity.setName(namePrefix + "_" + index);
|
entity.setName(namePrefix + "_" + index);
|
||||||
entity.setUserId(dto.getUserId());
|
entity.setUserId(dto.getUserId());
|
||||||
entity.setTrainStatus(0); // 默认训练中
|
entity.setTrainStatus(0); // 默认训练中
|
||||||
|
batchInsertList.add(entity);
|
||||||
baseDao.insert(entity);
|
}
|
||||||
|
if (ToolUtil.isNotEmpty(batchInsertList)) {
|
||||||
|
insertBatch(batchInsertList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +191,11 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
List<VoiceCloneResponseDTO> dtoList = new ArrayList<>(entityList.size());
|
List<VoiceCloneResponseDTO> dtoList = new ArrayList<>(entityList.size());
|
||||||
|
|
||||||
|
// 获取用户名称ID集合
|
||||||
|
Set<Long> userIdList = entityList.stream().map(VoiceCloneEntity::getUserId).collect(Collectors.toSet());
|
||||||
|
List<SysUserEntity> userList = sysUserDao.selectList(new QueryWrapper<SysUserEntity>().in("id", userIdList));
|
||||||
|
Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(SysUserEntity::getId, SysUserEntity::getUsername));
|
||||||
|
|
||||||
// 转换每个实体为DTO
|
// 转换每个实体为DTO
|
||||||
for (VoiceCloneEntity entity : entityList) {
|
for (VoiceCloneEntity entity : entityList) {
|
||||||
VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
|
VoiceCloneResponseDTO dto = ConvertUtils.sourceToTarget(entity, VoiceCloneResponseDTO.class);
|
||||||
@@ -198,7 +207,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
// 设置用户名称
|
// 设置用户名称
|
||||||
if (entity.getUserId() != null) {
|
if (entity.getUserId() != null) {
|
||||||
dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername());
|
dto.setUserName(userMap.get(entity.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
|
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- 更新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": "enable_ws_reuse", "type": "boolean", "label": "是否开启链接复用", "default": true}, {"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. 链接复用:开启WebSocket连接复用,默认true减少链接损耗(注意:复用后设备处于聆听状态时空闲链接会占并发数)
|
||||||
|
7. 语速:-50~100,可不填,正常默认值0,可填-50~100
|
||||||
|
8. 音量:-50~100,可不填,正常默认值0,可填-50~100
|
||||||
|
9. 音高:-12~12,可不填,正常默认值0,可填-12~12
|
||||||
|
10. 填入配置文件中' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (311, 'enable_websocket_ping', 'false', 'boolean', 1, '是否启用WebSocket心跳保活机制');
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- 为智能体聊天历史记录添加音频ID索引
|
||||||
|
ALTER TABLE ai_agent_chat_history ADD INDEX idx_ai_agent_chat_history_audio_id (audio_id);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
-- 更新豆包流式ASR供应器,增加end_window_size配置
|
||||||
|
delete from `ai_model_provider` where id = 'SYSTEM_ASR_DoubaoStreamASR';
|
||||||
|
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
('SYSTEM_ASR_DoubaoStreamASR', 'ASR', 'doubao_stream', '火山引擎语音识别(流式)', '[{"key":"appid","label":"应用ID","type":"string"},{"key":"access_token","label":"访问令牌","type":"string"},{"key":"cluster","label":"集群","type":"string"},{"key":"boosting_table_name","label":"热词文件名称","type":"string"},{"key":"correct_table_name","label":"替换词文件名称","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"end_window_size","label":"静音判定时长(ms)","type":"number"}]', 3, 1, NOW(), 1, NOW());
|
||||||
|
|
||||||
|
|
||||||
|
-- 更新豆包流式ASR模型配置,增加end_window_size默认值
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`config_json` = JSON_SET(`config_json`, '$.end_window_size', 200)
|
||||||
|
WHERE `id` = 'ASR_DoubaoStreamASR' AND JSON_EXTRACT(`config_json`, '$.end_window_size') IS NULL;
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
-- 添加阿里百炼Paraformer实时语音识别服务配置
|
||||||
|
delete from `ai_model_provider` where id = 'SYSTEM_ASR_AliyunBLStream';
|
||||||
|
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
('SYSTEM_ASR_AliyunBLStream', 'ASR', 'aliyunbl_stream', '阿里百炼Paraformer实时语音识别', '[{"key":"api_key","label":"API密钥","type":"password"},{"key":"model","label":"模型名称","type":"string"},{"key":"format","label":"音频格式","type":"string"},{"key":"sample_rate","label":"采样率","type":"number"},{"key":"output_dir","label":"输出目录","type":"string"}]', 18, 1, NOW(), 1, NOW());
|
||||||
|
|
||||||
|
delete from `ai_model_config` where id = 'ASR_AliyunBLStream';
|
||||||
|
INSERT INTO `ai_model_config` VALUES ('ASR_AliyunBLStream', 'ASR', 'AliyunBLStream', '阿里百炼Paraformer实时语音识别', 0, 1, '{"type": "aliyunbl_stream", "api_key": "", "model": "paraformer-realtime-v2", "format": "pcm", "sample_rate": 16000, "disfluency_removal_enabled": false, "semantic_punctuation_enabled": false, "max_sentence_silence": 200, "multi_threshold_mode_enabled": false, "punctuation_prediction_enabled": true, "inverse_text_normalization_enabled": true, "output_dir": "tmp/"}', 'https://help.aliyun.com/zh/model-studio/websocket-for-paraformer-real-time-service', '支持多语言、热词定制、语义断句等高级功能', 21, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
-- 更新阿里百炼Paraformer模型配置的说明文档
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`doc_link` = 'https://help.aliyun.com/zh/model-studio/websocket-for-paraformer-real-time-service',
|
||||||
|
`remark` = '阿里百炼Paraformer实时语音识别配置说明:
|
||||||
|
1. 登录阿里云百炼平台 https://bailian.console.aliyun.com/
|
||||||
|
2. 创建API-KEY https://bailian.console.aliyun.com/#/api-key
|
||||||
|
3. 支持模型:paraformer-realtime-v2(推荐)、paraformer-realtime-8k-v2、paraformer-realtime-v1、paraformer-realtime-8k-v1
|
||||||
|
4. 功能特性:
|
||||||
|
- 多语言支持(中文含方言、英文、日语、韩语、德语、法语、俄语)
|
||||||
|
- 热词定制(vocabulary_id参数),详细说明请参考:https://help.aliyun.com/zh/model-studio/custom-hot-words?
|
||||||
|
- 语义断句/VAD断句(semantic_punctuation_enabled参数)
|
||||||
|
- 自动标点符号、ITN、过滤语气词等
|
||||||
|
5. 参数说明:
|
||||||
|
- model: 模型名称,推荐paraformer-realtime-v2
|
||||||
|
- sample_rate: 采样率(Hz),v2支持任意采样率,v1仅支持16000,8k版本仅支持8000
|
||||||
|
- semantic_punctuation_enabled: false为VAD断句(低延迟),true为语义断句(高准确)
|
||||||
|
- max_sentence_silence: VAD断句静音时长阈值(200-6000ms)
|
||||||
|
' WHERE `id` = 'ASR_AliyunBLStream';
|
||||||
|
|
||||||
|
|
||||||
|
-- 更新豆包流式ASR供应器,增加配置
|
||||||
|
delete from `ai_model_provider` where id = 'SYSTEM_ASR_DoubaoStreamASR';
|
||||||
|
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
|
||||||
|
('SYSTEM_ASR_DoubaoStreamASR', 'ASR', 'doubao_stream', '火山引擎语音识别(流式)', '[{"key":"appid","label":"应用ID","type":"string"},{"key":"access_token","label":"访问令牌","type":"string"},{"key":"cluster","label":"集群","type":"string"},{"key":"boosting_table_name","label":"热词文件名称","type":"string"},{"key":"correct_table_name","label":"替换词文件名称","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"end_window_size","label":"静音判定时长(ms)","type":"number"},{"key":"enable_multilingual","label":"是否开启多语种识别模式","type":"boolean"},{"key":"language","label":"指定语言编码","type":"string"}]', 3, 1, NOW(), 1, NOW());
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`remark` = '豆包ASR配置说明:
|
||||||
|
1. 豆包ASR和豆包(流式)ASR的区别是:豆包ASR是按次收费,豆包(流式)ASR是按时收费
|
||||||
|
2. 一般来说按次收费的更便宜,但是豆包(流式)ASR使用了大模型技术,效果更好
|
||||||
|
3. 需要在火山引擎控制台创建应用并获取appid和access_token
|
||||||
|
4. 支持中文语音识别
|
||||||
|
5. 需要网络连接
|
||||||
|
6. 输出文件保存在tmp/目录
|
||||||
|
申请步骤:
|
||||||
|
1. 访问 https://console.volcengine.com/speech/app
|
||||||
|
2. 创建新应用
|
||||||
|
3. 获取appid和access_token
|
||||||
|
4. 填入配置文件中
|
||||||
|
如需设置热词,请参考:https://www.volcengine.com/docs/6561/155738
|
||||||
|
如开启多语种识别模式,请设置language当该键为空时,该模型支持中英文、上海话、闽南语,四川、陕西、粤语识别。其他语种请参考:https://www.volcengine.com/docs/6561/1354869
|
||||||
|
' WHERE `id` = 'ASR_DoubaoStreamASR';
|
||||||
|
|
||||||
|
-- 更新豆包流式ASR模型配置,增加enable_multilingual默认值
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`config_json` = JSON_SET(
|
||||||
|
`config_json`,
|
||||||
|
'$.enable_multilingual', false,
|
||||||
|
'$.language', 'zh-CN'
|
||||||
|
)
|
||||||
|
WHERE `id` = 'ASR_DoubaoStreamASR'
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.enable_multilingual') IS NULL
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.language') IS NULL;
|
||||||
|
|
||||||
|
|
||||||
|
-- 更新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": "enable_ws_reuse", "type": "boolean", "label": "是否开启链接复用", "default": true}, {"key": "speech_rate", "type": "number", "label": "语速(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "音量(-50~100)"}, {"key": "pitch", "type": "number", "label": "音高(-12~12)"}, {"key": "emotion_scale", "type": "number", "label": "情感强度(1-5)"}, {"key": "emotion", "type": "string", "label": "情感类型"}]'
|
||||||
|
WHERE `id` = 'SYSTEM_TTS_HSDSTTS';
|
||||||
|
|
||||||
|
-- 更新默认值
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`config_json` = JSON_SET(
|
||||||
|
`config_json`,
|
||||||
|
'$.emotion', 'neutral',
|
||||||
|
'$.emotion_scale', 4
|
||||||
|
)
|
||||||
|
WHERE `id` = 'TTS_HuoshanDoubleStreamTTS'
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.emotion') IS NULL
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.emotion_scale') IS NULL;
|
||||||
|
|
||||||
|
-- 增加文档链接和备注
|
||||||
|
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. 链接复用:开启WebSocket连接复用,默认true减少链接损耗(注意:复用后设备处于聆听状态时空闲链接会占并发数)
|
||||||
|
7. 语速:-50~100,可不填,正常默认值0,可填-50~100
|
||||||
|
8. 音量:-50~100,可不填,正常默认值0,可填-50~100
|
||||||
|
9. 音高:-12~12,可不填,正常默认值0,可填-12~12
|
||||||
|
10. 多情感参数(当前仅部分音色支持设置情感):
|
||||||
|
相关音色列表:https://www.volcengine.com/docs/6561/1257544
|
||||||
|
- emotion_scale:情感强度,可选值为:1~5,默认值为4
|
||||||
|
- emotion:情感类型,可选值为:neutral、happy、sad、angry、fearful、disgusted、surprised
|
||||||
|
' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- 更改FunASRServer说明文档模型为SenseVoiceSmall
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`doc_link` = 'https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md',
|
||||||
|
`remark` = '独立部署FunASR,使用FunASR的API服务,只需要五句话
|
||||||
|
第一句:mkdir -p ./funasr-runtime-resources/models
|
||||||
|
第二句:sudo docker run -d -p 10096:10095 --privileged=true -v $PWD/funasr-runtime-resources/models:/workspace/models registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12
|
||||||
|
上一句话执行后会进入到容器,继续第三句:cd FunASR/runtime
|
||||||
|
不要退出容器,继续在容器中执行第四句:nohup bash run_server_2pass.sh --download-model-dir /workspace/models --vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx --model-dir iic/SenseVoiceSmall-onnx --online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx --punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx --lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst --itn-dir thuduj12/fst_itn_zh --hotword /workspace/models/hotwords.txt > log.txt 2>&1 &
|
||||||
|
上一句话执行后会进入到容器,继续第五句:tail -f log.txt
|
||||||
|
第五句话执行完后,会看到模型下载日志,下载完后就可以连接使用了
|
||||||
|
以上是使用CPU推理,如果有GPU,详细参考:https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md' WHERE `id` = 'ASR_FunASRServer';
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
-- 批量清理 ai_model_provider 中的 sample_rate 字段定义
|
||||||
|
UPDATE `ai_model_provider` ap
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
JSON_ARRAYAGG(
|
||||||
|
JSON_OBJECT('key', jt.k, 'label', jt.l, 'type', jt.t)
|
||||||
|
) AS new_fields
|
||||||
|
FROM `ai_model_provider`,
|
||||||
|
JSON_TABLE(`fields`, '$[*]' COLUMNS (
|
||||||
|
k VARCHAR(50) PATH '$.key',
|
||||||
|
l VARCHAR(100) PATH '$.label',
|
||||||
|
t VARCHAR(20) PATH '$.type'
|
||||||
|
)) AS jt
|
||||||
|
WHERE `model_type` = 'TTS'
|
||||||
|
AND jt.k != 'sample_rate'
|
||||||
|
GROUP BY id
|
||||||
|
) filtered ON ap.id = filtered.id
|
||||||
|
SET ap.fields = filtered.new_fields;
|
||||||
|
|
||||||
|
-- 清理 config_json 顶层的 sample_rate
|
||||||
|
UPDATE `ai_model_config`
|
||||||
|
SET `config_json` = JSON_REMOVE(`config_json`, '$.sample_rate')
|
||||||
|
WHERE `model_type` = 'TTS'
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.sample_rate') IS NOT NULL;
|
||||||
|
|
||||||
|
-- 清理Minimax流式TTS的sample_rate参数(位于audio_setting内部)
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`config_json` = JSON_SET(`config_json`, '$.audio_setting', JSON_REMOVE(JSON_EXTRACT(`config_json`, '$.audio_setting'), '$.sample_rate'))
|
||||||
|
WHERE `id` = 'TTS_MinimaxStreamTTS'
|
||||||
|
AND JSON_EXTRACT(`config_json`, '$.audio_setting.sample_rate') IS NOT NULL;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
-- 更新HuoshanDoubleStreamTTS供应器配置,将分散的参数改为JSON字典配置
|
||||||
|
-- 将 speech_rate, loudness_rate, pitch, emotion, emotion_scale 等参数整合为 audio_params, additions, mix_speaker 三个JSON字典
|
||||||
|
|
||||||
|
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": "enable_ws_reuse", "type": "boolean", "label": "是否开启链接复用", "default": true},
|
||||||
|
{"key": "audio_params", "type": "dict", "label": "音频输出配置"},
|
||||||
|
{"key": "additions", "type": "dict", "label": "高级文本处理配置"},
|
||||||
|
{"key": "mix_speaker", "type": "dict", "label": "混音控制配置"}
|
||||||
|
]'
|
||||||
|
WHERE `id` = 'SYSTEM_TTS_HSDSTTS';
|
||||||
|
|
||||||
|
-- 更新现有配置,将旧的分散参数迁移到新的JSON字典结构
|
||||||
|
UPDATE `ai_model_config`
|
||||||
|
SET `config_json` = JSON_SET(
|
||||||
|
`config_json`,
|
||||||
|
'$.audio_params', JSON_OBJECT(
|
||||||
|
'speech_rate', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.speech_rate')), ''), '0') AS SIGNED),
|
||||||
|
'loudness_rate', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.loudness_rate')), ''), '0') AS SIGNED)
|
||||||
|
),
|
||||||
|
'$.additions', JSON_OBJECT(
|
||||||
|
'aigc_metadata', JSON_OBJECT(),
|
||||||
|
'cache_config', JSON_OBJECT(),
|
||||||
|
'post_process', JSON_OBJECT(
|
||||||
|
'pitch', CAST(COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.pitch')), ''), '0') AS SIGNED)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'$.mix_speaker', JSON_OBJECT()
|
||||||
|
)
|
||||||
|
WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
|
||||||
|
|
||||||
|
-- 删除旧的分散参数字段
|
||||||
|
UPDATE `ai_model_config`
|
||||||
|
SET `config_json` = JSON_REMOVE(
|
||||||
|
`config_json`,
|
||||||
|
'$.speech_rate',
|
||||||
|
'$.loudness_rate',
|
||||||
|
'$.pitch',
|
||||||
|
'$.emotion',
|
||||||
|
'$.emotion_scale'
|
||||||
|
)
|
||||||
|
WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
|
||||||
|
|
||||||
|
-- 更新文档链接和备注说明
|
||||||
|
UPDATE `ai_model_config` SET
|
||||||
|
`doc_link` = 'https://www.volcengine.com/docs/6561/1329505',
|
||||||
|
`remark` = '火山引擎双向流式TTS配置说明:
|
||||||
|
1. 访问 https://www.volcengine.com/ 注册并开通火山引擎账号
|
||||||
|
2. 访问 https://console.volcengine.com/speech/service/10007 开通语音合成大模型,购买音色
|
||||||
|
3. 在页面底部获取appid和access_token
|
||||||
|
4. 资源ID固定为:volc.service_type.10029(大模型语音合成及混音)
|
||||||
|
5. 链接复用:开启WebSocket连接复用,默认true减少链接损耗(注意:复用后设备处于聆听状态时空闲链接会占并发数)
|
||||||
|
|
||||||
|
详细参数文档:https://www.volcengine.com/docs/6561/1329505
|
||||||
|
【audio_params】音频输出配置 - 用户可自定义添加火山引擎支持的任何音频参数
|
||||||
|
- speech_rate: 语速(-50~100),默认0
|
||||||
|
- loudness_rate: 音量(-50~100),默认0
|
||||||
|
- emotion: 情感类型(仅部分音色支持),可选值:neutral、happy、sad、angry、fearful、disgusted、surprised
|
||||||
|
- emotion_scale: 情感强度(1~5),默认4
|
||||||
|
示例:{"speech_rate": 10, "loudness_rate": 5, "emotion": "happy", "emotion_scale": 4}
|
||||||
|
|
||||||
|
【additions】高级文本处理配置 - 用户可自定义添加火山引擎支持的任何高级参数
|
||||||
|
- post_process.pitch: 音高(-12~12),默认0
|
||||||
|
- aigc_metadata: AIGC元数据配置
|
||||||
|
- cache_config: 缓存配置
|
||||||
|
示例:{"post_process": {"pitch": 2}, "aigc_metadata": {}, "cache_config": {}}
|
||||||
|
|
||||||
|
【mix_speaker】混音控制配置 - 多音色混合(仅 TTS 1.0)
|
||||||
|
示例:
|
||||||
|
{"speakers": [
|
||||||
|
{"source_speaker": "zh_male_bvlazysheep","mix_factor": 0.3},
|
||||||
|
{"source_speaker": "BV120_streaming","mix_factor": 0.3},
|
||||||
|
{"source_speaker": "zh_male_ahu_conversation_wvae_bigtts","mix_factor": 0.4}
|
||||||
|
]}
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 多情感音色参数(emotion、emotion_scale)仅部分音色支持
|
||||||
|
- 相关音色列表:https://www.volcengine.com/docs/6561/1257544
|
||||||
|
- 用户可根据火山引擎API文档自行添加更多参数
|
||||||
|
- 混音功能主要适用于豆包语音合成模型1.0的音色,使用时需要将req_params.speaker设置为custom_mix_bigtts
|
||||||
|
'
|
||||||
|
WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
-- 更新小智参数中的默认采样率从 16000 改为 24000
|
||||||
|
UPDATE `sys_params`
|
||||||
|
SET `param_value` = '{
|
||||||
|
"type": "hello",
|
||||||
|
"version": 1,
|
||||||
|
"transport": "websocket",
|
||||||
|
"audio_params": {
|
||||||
|
"format": "opus",
|
||||||
|
"sample_rate": 24000,
|
||||||
|
"channels": 1,
|
||||||
|
"frame_duration": 60
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
WHERE `id` = 309 AND `param_code` = 'xiaozhi';
|
||||||
@@ -424,6 +424,13 @@ databaseChangeLog:
|
|||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202511131023.sql
|
path: classpath:db/changelog/202511131023.sql
|
||||||
- changeSet:
|
- changeSet:
|
||||||
|
id: 202511221450
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202511221450.sql
|
||||||
|
- changeSet:
|
||||||
id: 202512031517
|
id: 202512031517
|
||||||
author: rainv123
|
author: rainv123
|
||||||
changes:
|
changes:
|
||||||
@@ -445,3 +452,59 @@ databaseChangeLog:
|
|||||||
- sqlFile:
|
- sqlFile:
|
||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202512131453.sql
|
path: classpath:db/changelog/202512131453.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202512161529
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202512161529.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202512192245
|
||||||
|
author: hrz
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202512192245.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202512221117
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202512221117.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202512301430
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202512301430.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202601051433
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202601051433.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202601141645
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202601141645.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202601231530
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202601231530.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202601261730
|
||||||
|
author: RanChen
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202601261730.sql
|
||||||
|
|||||||
@@ -198,4 +198,7 @@
|
|||||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
||||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
||||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||||
|
10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
|
10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
|
||||||
|
10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
|
||||||
@@ -8,23 +8,23 @@
|
|||||||
10004=Die Kontonummer oder das Passwort ist falsch
|
10004=Die Kontonummer oder das Passwort ist falsch
|
||||||
10005=Konto wurde deaktiviert
|
10005=Konto wurde deaktiviert
|
||||||
10006=Eindeutige ID darf nicht leer sein
|
10006=Eindeutige ID darf nicht leer sein
|
||||||
10007=Der Bestätigungscode ist falsch
|
10007=Der Best\u00E4tigungscode ist falsch
|
||||||
10008=Zuerst Untermenü oder Schaltfläche löschen
|
10008=Zuerst Untermen\u00FC oder Schaltfl\u00E4che l\u00F6schen
|
||||||
10009=Das ursprüngliche Passwort ist falsch
|
10009=Das urspr\u00FCngliche Passwort ist falsch
|
||||||
10010=Das Konto oder Passwort ist falsch, Sie können es noch {0} Mal versuchen
|
10010=Das Konto oder Passwort ist falsch, Sie k\u00F6nnen es noch {0} Mal versuchen
|
||||||
|
|
||||||
10011=Die übergeordnete Abteilung hat eine falsche Wahl getroffen
|
10011=Die \u00FCbergeordnete Abteilung hat eine falsche Wahl getroffen
|
||||||
10012=Oberes Menü kann nicht sich selbst sein
|
10012=Oberes Men\u00FC kann nicht sich selbst sein
|
||||||
10013=Datenberechtigungsschnittstelle, die nur ein Map-Typ-Parameter sein kann
|
10013=Datenberechtigungsschnittstelle, die nur ein Map-Typ-Parameter sein kann
|
||||||
10014=Bitte löschen Sie zuerst die untergeordnete Abteilung
|
10014=Bitte l\u00F6schen Sie zuerst die untergeordnete Abteilung
|
||||||
10015=Bitte löschen Sie zuerst den Benutzer unter der Abteilung
|
10015=Bitte l\u00F6schen Sie zuerst den Benutzer unter der Abteilung
|
||||||
10016=Bereitstellung fehlgeschlagen, kein Prozess
|
10016=Bereitstellung fehlgeschlagen, kein Prozess
|
||||||
10017=Das ModellDiagramm ist falsch, bitte überprüfen
|
10017=Das ModellDiagramm ist falsch, bitte \u00FCberpr\u00FCfen
|
||||||
10018=Export fehlgeschlagen, Modell-ID ist {0}
|
10018=Export fehlgeschlagen, Modell-ID ist {0}
|
||||||
|
|
||||||
10019=Bitte laden Sie eine Datei hoch
|
10019=Bitte laden Sie eine Datei hoch
|
||||||
10020=Token darf nicht leer sein
|
10020=Token darf nicht leer sein
|
||||||
10021=Token ist ungültig, bitte melden Sie sich erneut an
|
10021=Token ist ung\u00FCltig, bitte melden Sie sich erneut an
|
||||||
10022=Das Konto wurde gesperrt
|
10022=Das Konto wurde gesperrt
|
||||||
10023=Bitte laden Sie Dateien im Format zip, bar, bpmn, bpmn20.xml hoch
|
10023=Bitte laden Sie Dateien im Format zip, bar, bpmn, bpmn20.xml hoch
|
||||||
|
|
||||||
@@ -37,25 +37,25 @@
|
|||||||
10029=Darf keine illegalen Zeichen enthalten
|
10029=Darf keine illegalen Zeichen enthalten
|
||||||
|
|
||||||
10030=Das Passwort hat weniger als {0} Ziffern
|
10030=Das Passwort hat weniger als {0} Ziffern
|
||||||
10031=Das Passwort muss gleichzeitig aus Zahlen, Groß- und Kleinbuchstaben sowie Sonderzeichen bestehen
|
10031=Das Passwort muss gleichzeitig aus Zahlen, Gro\u00DF- und Kleinbuchstaben sowie Sonderzeichen bestehen
|
||||||
10032=Ausnahme beim Löschen dieser Daten
|
10032=Ausnahme beim L\u00F6schen dieser Daten
|
||||||
10033=Gerätebestätigungscode Fehler
|
10033=Ger\u00E4tebest\u00E4tigungscode Fehler
|
||||||
|
|
||||||
10034=Parameterwert darf nicht leer sein
|
10034=Parameterwert darf nicht leer sein
|
||||||
10035=Parametertyp darf nicht leer sein
|
10035=Parametertyp darf nicht leer sein
|
||||||
10036=Nicht unterstützter Parametertyp
|
10036=Nicht unterst\u00FCtzter Parametertyp
|
||||||
10037=Parameterwert muss eine gültige Zahl sein
|
10037=Parameterwert muss eine g\u00FCltige Zahl sein
|
||||||
10038=Parameterwert muss true oder false sein
|
10038=Parameterwert muss true oder false sein
|
||||||
10039=Parameterwert muss ein gültiges JSON-Array-Format sein
|
10039=Parameterwert muss ein g\u00FCltiges JSON-Array-Format sein
|
||||||
10040=Parameterwert muss ein gültiges JSON-Format sein
|
10040=Parameterwert muss ein g\u00FCltiges JSON-Format sein
|
||||||
|
|
||||||
10041=Gerät nicht gefunden
|
10041=Ger\u00E4t nicht gefunden
|
||||||
10042={0}
|
10042={0}
|
||||||
10043=Löschen der Daten fehlgeschlagen
|
10043=L\u00F6schen der Daten fehlgeschlagen
|
||||||
10044=Benutzer nicht angemeldet
|
10044=Benutzer nicht angemeldet
|
||||||
10045=WebSocket-Verbindung fehlgeschlagen oder Zeitüberschreitung
|
10045=WebSocket-Verbindung fehlgeschlagen oder Zeit\u00FCberschreitung
|
||||||
10046=Stimmabdruck speichern fehlgeschlagen, bitte Administrator kontaktieren
|
10046=Stimmabdruck speichern fehlgeschlagen, bitte Administrator kontaktieren
|
||||||
10047=Tägliches SendeLimit erreicht
|
10047=T\u00E4gliches SendeLimit erreicht
|
||||||
10048=Altes Passwort ist falsch
|
10048=Altes Passwort ist falsch
|
||||||
10049=Das eingestellte LLM ist nicht openai oder ollama
|
10049=Das eingestellte LLM ist nicht openai oder ollama
|
||||||
10050=Token-Generierung fehlgeschlagen
|
10050=Token-Generierung fehlgeschlagen
|
||||||
@@ -65,45 +65,45 @@
|
|||||||
10054=Stimmabdruck-Schnittstelle nicht konfiguriert, bitte konfigurieren Sie zuerst die Stimmabdruck-Schnittstellenadresse (server.voice_print) in der Parameterkonfiguration
|
10054=Stimmabdruck-Schnittstelle nicht konfiguriert, bitte konfigurieren Sie zuerst die Stimmabdruck-Schnittstellenadresse (server.voice_print) in der Parameterkonfiguration
|
||||||
10055=SMS-Sendung fehlgeschlagen
|
10055=SMS-Sendung fehlgeschlagen
|
||||||
10056=SMS-Verbindung konnte nicht hergestellt werden
|
10056=SMS-Verbindung konnte nicht hergestellt werden
|
||||||
10057=Stimmabdruck für Agent konnte nicht erstellt werden
|
10057=Stimmabdruck f\u00FCr Agent konnte nicht erstellt werden
|
||||||
10058=Stimmabdruck für Agent konnte nicht aktualisiert werden
|
10058=Stimmabdruck f\u00FCr Agent konnte nicht aktualisiert werden
|
||||||
10059=Stimmabdruck für Agent konnte nicht gelöscht werden
|
10059=Stimmabdruck f\u00FCr Agent konnte nicht gel\u00F6scht werden
|
||||||
10060=Sendung zu häufig, bitte versuchen Sie es nach {0} Sekunden erneut
|
10060=Sendung zu h\u00E4ufig, bitte versuchen Sie es nach {0} Sekunden erneut
|
||||||
10061=Aktivierungscode darf nicht leer sein
|
10061=Aktivierungscode darf nicht leer sein
|
||||||
10062=Aktivierungscode Fehler
|
10062=Aktivierungscode Fehler
|
||||||
10063=Gerät bereits aktiviert
|
10063=Ger\u00E4t bereits aktiviert
|
||||||
10064=Dieses Modell ist das Standardmodell, bitte setzen Sie zuerst ein anderes Modell als Standard
|
10064=Dieses Modell ist das Standardmodell, bitte setzen Sie zuerst ein anderes Modell als Standard
|
||||||
10065=Hinzufügen der Daten fehlgeschlagen
|
10065=Hinzuf\u00FCgen der Daten fehlgeschlagen
|
||||||
10066=Aktualisieren der Daten fehlgeschlagen
|
10066=Aktualisieren der Daten fehlgeschlagen
|
||||||
10067=Grafischer Bestätigungscode Fehler
|
10067=Grafischer Best\u00E4tigungscode Fehler
|
||||||
10068=Mobilregistrierung nicht aktiviert, SMS-Bestätigungscode-Funktion kann nicht verwendet werden
|
10068=Mobilregistrierung nicht aktiviert, SMS-Best\u00E4tigungscode-Funktion kann nicht verwendet werden
|
||||||
10069=Benutzername ist keine Handynummer, bitte erneut eingeben
|
10069=Benutzername ist keine Handynummer, bitte erneut eingeben
|
||||||
10070=Diese Handynummer wurde bereits registriert
|
10070=Diese Handynummer wurde bereits registriert
|
||||||
10071=Die eingegebene Handynummer ist nicht registriert
|
10071=Die eingegebene Handynummer ist nicht registriert
|
||||||
10072=Normale Benutzerregistrierung derzeit nicht erlaubt
|
10072=Normale Benutzerregistrierung derzeit nicht erlaubt
|
||||||
10073=Mobilregistrierung nicht aktiviert, Passwort-Wiederherstellungsfunktion kann nicht verwendet werden
|
10073=Mobilregistrierung nicht aktiviert, Passwort-Wiederherstellungsfunktion kann nicht verwendet werden
|
||||||
10074=Das Format der eingegebenen Handynummer ist falsch
|
10074=Das Format der eingegebenen Handynummer ist falsch
|
||||||
10075=Der eingegebene SMS-Bestätigungscode ist falsch
|
10075=Der eingegebene SMS-Best\u00E4tigungscode ist falsch
|
||||||
10076=Wörterbuchtyp existiert nicht
|
10076=W\u00F6rterbuchtyp existiert nicht
|
||||||
10077=Wörterbuchtypcode ist dupliziert
|
10077=W\u00F6rterbuchtypcode ist dupliziert
|
||||||
10078=Lesen der Ressource fehlgeschlagen
|
10078=Lesen der Ressource fehlgeschlagen
|
||||||
10079=LLM-Modell und Absichtserkennung, Parameterauswahl stimmt nicht überein
|
10079=LLM-Modell und Absichtserkennung, Parameterauswahl stimmt nicht \u00FCberein
|
||||||
10080=Dieser Stimmabdruck gehört zu {0}, der bereits registriert ist, bitte wählen Sie eine andere Stimme
|
10080=Dieser Stimmabdruck geh\u00F6rt zu {0}, der bereits registriert ist, bitte w\u00E4hlen Sie eine andere Stimme
|
||||||
10081=Fehler beim Löschen des Stimmabdrucks aufgetreten
|
10081=Fehler beim L\u00F6schen des Stimmabdrucks aufgetreten
|
||||||
10082=Änderung nicht erlaubt, diese Stimme wurde bereits als Stimmabdruck registriert ({0})
|
10082=\u00C4nderung nicht erlaubt, diese Stimme wurde bereits als Stimmabdruck registriert ({0})
|
||||||
10083=Fehler beim Ändern des Stimmabdrucks, bitte Administrator kontaktieren
|
10083=Fehler beim \u00C4ndern des Stimmabdrucks, bitte Administrator kontaktieren
|
||||||
10084=Stimmabdruck-Schnittstellenadresse Fehler, bitte gehen Sie zur Parameterverwaltung, um die Stimmabdruck-Schnittstellenadresse zu ändern
|
10084=Stimmabdruck-Schnittstellenadresse Fehler, bitte gehen Sie zur Parameterverwaltung, um die Stimmabdruck-Schnittstellenadresse zu \u00E4ndern
|
||||||
10085=Audiodaten gehören nicht zu diesem Agent
|
10085=Audiodaten geh\u00F6ren nicht zu diesem Agent
|
||||||
10086=Audiodaten sind leer, bitte überprüfen Sie hochgeladene Daten
|
10086=Audiodaten sind leer, bitte \u00FCberpr\u00FCfen Sie hochgeladene Daten
|
||||||
10087=Stimmabdruck-Registrierung fehlgeschlagen, Anfrage nicht erfolgreich
|
10087=Stimmabdruck-Registrierung fehlgeschlagen, Anfrage nicht erfolgreich
|
||||||
10088=Stimmabdruck-Registrierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
10088=Stimmabdruck-Registrierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
||||||
10089=Stimmabdruck-Stornierung fehlgeschlagen, Anfrage nicht erfolgreich
|
10089=Stimmabdruck-Stornierung fehlgeschlagen, Anfrage nicht erfolgreich
|
||||||
10090=Stimmabdruck-Stornierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
10090=Stimmabdruck-Stornierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
||||||
10091=Modellanbieter existiert nicht
|
10091=Modellanbieter existiert nicht
|
||||||
10092=Das konfigurierte LLM existiert nicht
|
10092=Das konfigurierte LLM existiert nicht
|
||||||
10093=Diese Modellkonfiguration wird von Agent {0} referenziert und kann nicht gelöscht werden
|
10093=Diese Modellkonfiguration wird von Agent {0} referenziert und kann nicht gel\u00F6scht werden
|
||||||
10094=Dieses LLM-Modell wird von der Absichtserkennungskonfiguration referenziert und kann nicht gelöscht werden
|
10094=Dieses LLM-Modell wird von der Absichtserkennungskonfiguration referenziert und kann nicht gel\u00F6scht werden
|
||||||
10095=Ungültige Serveroperation
|
10095=Ung\u00FCltige Serveroperation
|
||||||
10096=Server WebSocket-Adresse nicht konfiguriert
|
10096=Server WebSocket-Adresse nicht konfiguriert
|
||||||
10097=Ziel-WebSocket-Adresse existiert nicht
|
10097=Ziel-WebSocket-Adresse existiert nicht
|
||||||
10098=WebSocket-Adressliste darf nicht leer sein
|
10098=WebSocket-Adressliste darf nicht leer sein
|
||||||
@@ -115,47 +115,47 @@
|
|||||||
10104=OTA-Adresse muss mit http oder https beginnen
|
10104=OTA-Adresse muss mit http oder https beginnen
|
||||||
10105=OTA-Adresse muss mit /ota/ enden
|
10105=OTA-Adresse muss mit /ota/ enden
|
||||||
10106=OTA-Schnittstellenzugriff fehlgeschlagen
|
10106=OTA-Schnittstellenzugriff fehlgeschlagen
|
||||||
10107=OTA-Schnittstelle Rückgabeinhaltformat ist falsch
|
10107=OTA-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||||
10108=OTA-Schnittstellenvalidierung fehlgeschlagen
|
10108=OTA-Schnittstellenvalidierung fehlgeschlagen
|
||||||
10109=MCP-Adresse darf nicht leer sein
|
10109=MCP-Adresse darf nicht leer sein
|
||||||
10110=MCP-Adresse darf nicht localhost oder 127.0.0.1 verwenden
|
10110=MCP-Adresse darf nicht localhost oder 127.0.0.1 verwenden
|
||||||
10111=Keine gültige MCP-Adresse
|
10111=Keine g\u00FCltige MCP-Adresse
|
||||||
10112=MCP-Schnittstellenzugriff fehlgeschlagen
|
10112=MCP-Schnittstellenzugriff fehlgeschlagen
|
||||||
10113=MCP-Schnittstelle Rückgabeinhaltformat ist falsch
|
10113=MCP-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||||
10114=MCP-Schnittstellenvalidierung fehlgeschlagen
|
10114=MCP-Schnittstellenvalidierung fehlgeschlagen
|
||||||
10115=Stimmabdruck-Schnittstellenadresse darf nicht leer sein
|
10115=Stimmabdruck-Schnittstellenadresse darf nicht leer sein
|
||||||
10116=Stimmabdruck-Schnittstellenadresse darf nicht localhost oder 127.0.0.1 verwenden
|
10116=Stimmabdruck-Schnittstellenadresse darf nicht localhost oder 127.0.0.1 verwenden
|
||||||
10117=Keine gültige Stimmabdruck-Schnittstellenadresse
|
10117=Keine g\u00FCltige Stimmabdruck-Schnittstellenadresse
|
||||||
10118=Stimmabdruck-Schnittstellenadresse muss mit http oder https beginnen
|
10118=Stimmabdruck-Schnittstellenadresse muss mit http oder https beginnen
|
||||||
10119=Stimmabdruck-Schnittstellenzugriff fehlgeschlagen
|
10119=Stimmabdruck-Schnittstellenzugriff fehlgeschlagen
|
||||||
10120=Stimmabdruck-Schnittstelle Rückgabeinhaltformat ist falsch
|
10120=Stimmabdruck-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||||
10121=Stimmabdruck-Schnittstellenvalidierung fehlgeschlagen
|
10121=Stimmabdruck-Schnittstellenvalidierung fehlgeschlagen
|
||||||
10122=MQTT-Geheimnis darf nicht leer sein
|
10122=MQTT-Geheimnis darf nicht leer sein
|
||||||
10123=Ihr MQTT-Geheimnis ist nicht sicher, es muss mindestens 8 Zeichen lang sein und muss sowohl Groß- als auch Kleinbuchstaben enthalten
|
10123=Ihr MQTT-Geheimnis ist nicht sicher, es muss mindestens 8 Zeichen lang sein und muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
|
||||||
10124=Ihr MQTT-Geheimnis ist nicht sicher, MQTT-Geheimnis muss sowohl Groß- als auch Kleinbuchstaben enthalten
|
10124=Ihr MQTT-Geheimnis ist nicht sicher, MQTT-Geheimnis muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
|
||||||
10125=Ihr MQTT-Geheimnis enthält schwaches Passwort
|
10125=Ihr MQTT-Geheimnis enth\u00E4lt schwaches Passwort
|
||||||
10128=Wörterbuchlabel ist dupliziert
|
10128=W\u00F6rterbuchlabel ist dupliziert
|
||||||
10129=SM2-Schlüssel nicht konfiguriert
|
10129=SM2-Schl\u00FCssel nicht konfiguriert
|
||||||
10130=SM2-Entschlüsselung fehlgeschlagen
|
10130=SM2-Entschl\u00FCsselung fehlgeschlagen
|
||||||
10131=modelType und provideCode dürfen nicht leer sein
|
10131=modelType und provideCode d\u00FCrfen nicht leer sein
|
||||||
10132=Keine Berechtigung, den Chat-Verlauf dieses Agents anzuzeigen
|
10132=Keine Berechtigung, den Chat-Verlauf dieses Agents anzuzeigen
|
||||||
10133=Sitzungs-ID darf nicht leer sein
|
10133=Sitzungs-ID darf nicht leer sein
|
||||||
10134=Agent-ID darf nicht leer sein
|
10134=Agent-ID darf nicht leer sein
|
||||||
10135=Chat-Verlauf-Download fehlgeschlagen
|
10135=Chat-Verlauf-Download fehlgeschlagen
|
||||||
10136=Download-Link abgelaufen oder ungültig
|
10136=Download-Link abgelaufen oder ung\u00FCltig
|
||||||
10137=Download-Link ungültig
|
10137=Download-Link ung\u00FCltig
|
||||||
10138=Benutzer
|
10138=Benutzer
|
||||||
10139=Agent
|
10139=Agent
|
||||||
10140=Audio-Datei darf nicht leer sein
|
10140=Audio-Datei darf nicht leer sein
|
||||||
10141=Nur Audio-Dateien werden unterstützt
|
10141=Nur Audio-Dateien werden unterst\u00FCtzt
|
||||||
10142=Audio-Dateigröße darf 10MB nicht überschreiten
|
10142=Audio-Dateigr\u00F6\u00DFe darf 10MB nicht \u00FCberschreiten
|
||||||
10143=Upload fehlgeschlagen
|
10143=Upload fehlgeschlagen
|
||||||
10144=Stimmenklon-Datensatz existiert nicht
|
10144=Stimmenklon-Datensatz existiert nicht
|
||||||
10145=Stimmressourceninformationen dürfen nicht leer sein
|
10145=Stimmressourceninformationen d\u00FCrfen nicht leer sein
|
||||||
10146=TTS-Plattformname darf nicht leer sein
|
10146=TTS-Plattformname darf nicht leer sein
|
||||||
10147=Stimmen-ID darf nicht leer sein
|
10147=Stimmen-ID darf nicht leer sein
|
||||||
10148=Kontoinhaber darf nicht leer sein
|
10148=Kontoinhaber darf nicht leer sein
|
||||||
10149=Zu löschende Stimmressourcen-ID darf nicht leer sein
|
10149=Zu l\u00F6schende Stimmressourcen-ID darf nicht leer sein
|
||||||
10150=Sie haben keine Berechtigung, diesen Datensatz zu bearbeiten
|
10150=Sie haben keine Berechtigung, diesen Datensatz zu bearbeiten
|
||||||
10151=Bitte laden Sie zuerst eine Audio-Datei hoch
|
10151=Bitte laden Sie zuerst eine Audio-Datei hoch
|
||||||
10152=Modellkonfiguration nicht gefunden
|
10152=Modellkonfiguration nicht gefunden
|
||||||
@@ -179,18 +179,18 @@
|
|||||||
10170=Wissensbasis-Name existiert bereits
|
10170=Wissensbasis-Name existiert bereits
|
||||||
10171=RAG-Konfiguration base_url darf nicht leer sein
|
10171=RAG-Konfiguration base_url darf nicht leer sein
|
||||||
10172=RAG-Konfiguration api_key darf nicht leer sein
|
10172=RAG-Konfiguration api_key darf nicht leer sein
|
||||||
10173=RAG-Konfiguration api_key darf keinen Platzhalter enthalten, bitte ersetzen Sie mit tatsächlichem API-Schlüssel
|
10173=RAG-Konfiguration api_key darf keinen Platzhalter enthalten, bitte ersetzen Sie mit tats\u00E4chlichem API-Schl\u00FCssel
|
||||||
10174=RAG-Konfiguration base_url-Formatfehler, muss mit http oder https beginnen
|
10174=RAG-Konfiguration base_url-Formatfehler, muss mit http oder https beginnen
|
||||||
10175=Mac-Adresse darf nicht leer sein
|
10175=Mac-Adresse darf nicht leer sein
|
||||||
10176=RAG-Konfiguration dataset_id darf nicht leer sein
|
10176=RAG-Konfiguration dataset_id darf nicht leer sein
|
||||||
10177=RAG-Konfiguration model_id darf nicht leer sein
|
10177=RAG-Konfiguration model_id darf nicht leer sein
|
||||||
10178=RAG-Konfiguration dataset_id und model_id dürfen nicht leer sein
|
10178=RAG-Konfiguration dataset_id und model_id d\u00FCrfen nicht leer sein
|
||||||
10179=Dateiname darf nicht leer sein
|
10179=Dateiname darf nicht leer sein
|
||||||
10180=Dateiinhalt darf nicht leer sein
|
10180=Dateiinhalt darf nicht leer sein
|
||||||
10181=Stimmenklon-Name darf nicht leer sein
|
10181=Stimmenklon-Name darf nicht leer sein
|
||||||
10182=Stimmenklon-Audio nicht gefunden
|
10182=Stimmenklon-Audio nicht gefunden
|
||||||
10183=Standard-Agent-Vorlage nicht gefunden
|
10183=Standard-Agent-Vorlage nicht gefunden
|
||||||
10184=Nicht unterstützter Adaptertyp
|
10184=Nicht unterst\u00FCtzter Adaptertyp
|
||||||
10185=RAG-Konfigurationsvalidierung fehlgeschlagen
|
10185=RAG-Konfigurationsvalidierung fehlgeschlagen
|
||||||
10186=Adapter-Erstellung fehlgeschlagen
|
10186=Adapter-Erstellung fehlgeschlagen
|
||||||
10187=Adapter-Initialisierung fehlgeschlagen
|
10187=Adapter-Initialisierung fehlgeschlagen
|
||||||
@@ -198,4 +198,7 @@
|
|||||||
10189=Adapter-Operation fehlgeschlagen
|
10189=Adapter-Operation fehlgeschlagen
|
||||||
10190=Adapter nicht gefunden
|
10190=Adapter nicht gefunden
|
||||||
10191=Adapter-Cache-Fehler
|
10191=Adapter-Cache-Fehler
|
||||||
10192=Adaptertyp nicht gefunden
|
10192=Adaptertyp nicht gefunden
|
||||||
|
10193=Adapter-ID darf nicht leer sein
|
||||||
|
10194=Adapter nicht gefunden oder nicht online
|
||||||
|
10195=OTA-Upload-Anzahl \u00FCberschreitet das Limit
|
||||||
@@ -198,4 +198,7 @@
|
|||||||
10189=Adapter operation failed
|
10189=Adapter operation failed
|
||||||
10190=Adapter not found
|
10190=Adapter not found
|
||||||
10191=Adapter cache error
|
10191=Adapter cache error
|
||||||
10192=Adapter type not found
|
10192=Adapter type not found
|
||||||
|
10193=Device ID cannot be empty
|
||||||
|
10194=Device not found or not online
|
||||||
|
10195=OTA upload times exceed the limit
|
||||||
@@ -1,201 +1,204 @@
|
|||||||
#Tiếng Việt
|
#Ti\u1EBFng Vi\u1EC7t
|
||||||
500=Ngoại lệ nội bộ máy chủ
|
500=Ngo\u1EA1i l\u1EC7 n\u1ED9i b\u1ED9 m\u00E1y ch\u1EE7
|
||||||
401=Không được ủy quyền
|
401=Kh\u00F4ng \u0111\u01B0\u1EE3c \u1EE7y quy\u1EC1n
|
||||||
403=Truy cập bị từ chối, không có quyền
|
403=Truy c\u1EADp b\u1ECB t\u1EEB ch\u1ED1i, kh\u00F4ng c\u00F3 quy\u1EC1n
|
||||||
10001={0} không thể để trống
|
10001={0} kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10002=Bản ghi đã tồn tại trong cơ sở dữ liệu
|
10002=B\u1EA3n ghi \u0111\u00E3 t\u1ED3n t\u1EA1i trong c\u01A1 s\u1EDF d\u1EEF li\u1EC7u
|
||||||
10003=Không thể lấy tham số
|
10003=Kh\u00F4ng th\u1EC3 l\u1EA5y tham s\u1ED1
|
||||||
10004=Số tài khoản hoặc mật khẩu không chính xác
|
10004=S\u1ED1 t\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10005=Tài khoản đã bị vô hiệu hóa
|
10005=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB v\u00F4 hi\u1EC7u h\u00F3a
|
||||||
10006=ID duy nhất không thể để trống
|
10006=ID duy nh\u1EA5t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10007=Mã xác minh không chính xác
|
10007=M\u00E3 x\u00E1c minh kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10008=Trước tiên hãy xóa menu con hoặc nút
|
10008=Tr\u01B0\u1EDBc ti\u00EAn h\u00E3y x\u00F3a menu con ho\u1EB7c n\u00FAt
|
||||||
10009=Mật khẩu gốc không chính xác
|
10009=M\u1EADt kh\u1EA9u g\u1ED1c kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10010=Tài khoản hoặc mật khẩu không chính xác, bạn có thể thử thêm {0} lần nữa
|
10010=T\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c, b\u1EA1n c\u00F3 th\u1EC3 th\u1EED th\u00EAm {0} l\u1EA7n n\u1EEFa
|
||||||
|
|
||||||
10011=Phòng ban cấp trên đã lựa chọn sai
|
10011=Ph\u00F2ng ban c\u1EA5p tr\u00EAn \u0111\u00E3 l\u1EF1a ch\u1ECDn sai
|
||||||
10012=Menu trên không thể là chính nó
|
10012=Menu tr\u00EAn kh\u00F4ng th\u1EC3 l\u00E0 ch\u00EDnh n\u00F3
|
||||||
10013=Giao diện quyền dữ liệu, chỉ có thể là tham số kiểu Map
|
10013=Giao di\u1EC7n quy\u1EC1n d\u1EEF li\u1EC7u, ch\u1EC9 c\u00F3 th\u1EC3 l\u00E0 tham s\u1ED1 ki\u1EC3u Map
|
||||||
10014=Vui lòng xóa phòng ban cấp dưới trước
|
10014=Vui l\u00F2ng x\u00F3a ph\u00F2ng ban c\u1EA5p d\u01B0\u1EDBi tr\u01B0\u1EDBc
|
||||||
10015=Vui lòng xóa người dùng thuộc phòng ban trước
|
10015=Vui l\u00F2ng x\u00F3a ng\u01B0\u1EDDi d\u00F9ng thu\u1ED9c ph\u00F2ng ban tr\u01B0\u1EDBc
|
||||||
10016=Triển khai thất bại, không có quy trình
|
10016=Tri\u1EC3n khai th\u1EA5t b\u1EA1i, kh\u00F4ng c\u00F3 quy tr\u00ECnh
|
||||||
10017=Biểu đồ mô hình không chính xác, vui lòng kiểm tra
|
10017=Bi\u1EC3u \u0111\u1ED3 m\u00F4 h\u00ECnh kh\u00F4ng ch\u00EDnh x\u00E1c, vui l\u00F2ng ki\u1EC3m tra
|
||||||
10018=Xuất thất bại, ID mô hình là {0}
|
10018=Xu\u1EA5t th\u1EA5t b\u1EA1i, ID m\u00F4 h\u00ECnh l\u00E0 {0}
|
||||||
|
|
||||||
10019=Vui lòng tải lên tệp
|
10019=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p
|
||||||
10020=Token không thể để trống
|
10020=Token kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10021=Token không hợp lệ, vui lòng đăng nhập lại
|
10021=Token kh\u00F4ng h\u1EE3p l\u1EC7, vui l\u00F2ng \u0111\u0103ng nh\u1EADp l\u1EA1i
|
||||||
10022=Tài khoản đã bị khóa
|
10022=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB kh\u00F3a
|
||||||
10023=Vui lòng tải lên tệp ở định dạng zip, bar, bpmn, bpmn20.xml
|
10023=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u1EDF \u0111\u1ECBnh d\u1EA1ng zip, bar, bpmn, bpmn20.xml
|
||||||
|
|
||||||
10024=Tải lên tệp {0} thất bại
|
10024=T\u1EA3i l\u00EAn t\u1EC7p {0} th\u1EA5t b\u1EA1i
|
||||||
10025=Gửi SMS {0} thất bại
|
10025=G\u1EEDi SMS {0} th\u1EA5t b\u1EA1i
|
||||||
10026=Mẫu email không tồn tại
|
10026=M\u1EABu email kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
|
|
||||||
10027=Ngoại lệ dịch vụ Redis
|
10027=Ngo\u1EA1i l\u1EC7 d\u1ECBch v\u1EE5 Redis
|
||||||
10028=Tác vụ hẹn giờ thất bại
|
10028=T\u00E1c v\u1EE5 h\u1EB9n gi\u1EDD th\u1EA5t b\u1EA1i
|
||||||
10029=Không thể chứa ký tự không hợp lệ
|
10029=Kh\u00F4ng th\u1EC3 ch\u1EE9a k\u00FD t\u1EF1 kh\u00F4ng h\u1EE3p l\u1EC7
|
||||||
|
|
||||||
10030=Mật khẩu ít hơn {0} chữ số
|
10030=M\u1EADt kh\u1EA9u \u00EDt h\u01A1n {0} ch\u1EEF s\u1ED1
|
||||||
10031=Mật khẩu phải bao gồm số, chữ hoa, chữ thường và ký tự đặc biệt cùng một lúc
|
10031=M\u1EADt kh\u1EA9u ph\u1EA3i bao g\u1ED3m s\u1ED1, ch\u1EEF hoa, ch\u1EEF th\u01B0\u1EDDng v\u00E0 k\u00FD t\u1EF1 \u0111\u1EB7c bi\u1EC7t c\u00F9ng m\u1ED9t l\u00FAc
|
||||||
10032=Ngoại lệ khi xóa dữ liệu này
|
10032=Ngo\u1EA1i l\u1EC7 khi x\u00F3a d\u1EEF li\u1EC7u n\u00E0y
|
||||||
10033=Lỗi mã xác minh thiết bị
|
10033=L\u1ED7i m\u00E3 x\u00E1c minh thi\u1EBFt b\u1ECB
|
||||||
|
|
||||||
10034=Giá trị tham số không thể để trống
|
10034=Gi\u00E1 tr\u1ECB tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10035=Loại tham số không thể để trống
|
10035=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10036=Loại tham số không được hỗ trợ
|
10036=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
||||||
10037=Giá trị tham số phải là số hợp lệ
|
10037=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 s\u1ED1 h\u1EE3p l\u1EC7
|
||||||
10038=Giá trị tham số phải là true hoặc false
|
10038=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 true ho\u1EB7c false
|
||||||
10039=Giá trị tham số phải là định dạng mảng JSON hợp lệ
|
10039=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng m\u1EA3ng JSON h\u1EE3p l\u1EC7
|
||||||
10040=Giá trị tham số phải là định dạng JSON hợp lệ
|
10040=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng JSON h\u1EE3p l\u1EC7
|
||||||
|
|
||||||
10041=Không tìm thấy thiết bị
|
10041=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB
|
||||||
10042={0}
|
10042={0}
|
||||||
10043=Xóa dữ liệu thất bại
|
10043=X\u00F3a d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||||
10044=Người dùng chưa đăng nhập
|
10044=Ng\u01B0\u1EDDi d\u00F9ng ch\u01B0a \u0111\u0103ng nh\u1EADp
|
||||||
10045=Kết nối WebSocket thất bại hoặc hết thời gian chờ
|
10045=K\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i ho\u1EB7c h\u1EBFt th\u1EDDi gian ch\u1EDD
|
||||||
10046=Không thể lưu dấu giọng nói, vui lòng liên hệ quản trị viên
|
10046=Kh\u00F4ng th\u1EC3 l\u01B0u d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
|
||||||
10047=Đạt đến giới hạn gửi hàng ngày
|
10047=\u0110\u1EA1t \u0111\u1EBFn gi\u1EDBi h\u1EA1n g\u1EEDi h\u00E0ng ng\u00E0y
|
||||||
10048=Mật khẩu cũ không chính xác
|
10048=M\u1EADt kh\u1EA9u c\u0169 kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10049=LLM được đặt không phải là openai hoặc ollama
|
10049=LLM \u0111\u01B0\u1EE3c \u0111\u1EB7t kh\u00F4ng ph\u1EA3i l\u00E0 openai ho\u1EB7c ollama
|
||||||
10050=Không thể tạo token
|
10050=Kh\u00F4ng th\u1EC3 t\u1EA1o token
|
||||||
10051=Không tìm thấy tài nguyên
|
10051=Kh\u00F4ng t\u00ECm th\u1EA5y t\u00E0i nguy\u00EAn
|
||||||
10052=Không tìm thấy agent mặc định
|
10052=Kh\u00F4ng t\u00ECm th\u1EA5y agent m\u1EB7c \u0111\u1ECBnh
|
||||||
10053=Không tìm thấy agent
|
10053=Kh\u00F4ng t\u00ECm th\u1EA5y agent
|
||||||
10054=Giao diện dấu giọng nói chưa được cấu hình, vui lòng cấu hình địa chỉ giao diện dấu giọng nói (server.voice_print) trong cấu hình tham số trước
|
10054=Giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh, vui l\u00F2ng c\u1EA5u h\u00ECnh \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i (server.voice_print) trong c\u1EA5u h\u00ECnh tham s\u1ED1 tr\u01B0\u1EDBc
|
||||||
10055=Gửi SMS thất bại
|
10055=G\u1EEDi SMS th\u1EA5t b\u1EA1i
|
||||||
10056=Không thể thiết lập kết nối SMS
|
10056=Kh\u00F4ng th\u1EC3 thi\u1EBFt l\u1EADp k\u1EBFt n\u1ED1i SMS
|
||||||
10057=Không thể tạo dấu giọng nói cho agent
|
10057=Kh\u00F4ng th\u1EC3 t\u1EA1o d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||||
10058=Không thể cập nhật dấu giọng nói cho agent
|
10058=Kh\u00F4ng th\u1EC3 c\u1EADp nh\u1EADt d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||||
10059=Không thể xóa dấu giọng nói cho agent
|
10059=Kh\u00F4ng th\u1EC3 x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||||
10060=Gửi quá thường xuyên, vui lòng thử lại sau {0} giây
|
10060=G\u1EEDi qu\u00E1 th\u01B0\u1EDDng xuy\u00EAn, vui l\u00F2ng th\u1EED l\u1EA1i sau {0} gi\u00E2y
|
||||||
10061=Mã kích hoạt không thể để trống
|
10061=M\u00E3 k\u00EDch ho\u1EA1t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10062=Lỗi mã kích hoạt
|
10062=L\u1ED7i m\u00E3 k\u00EDch ho\u1EA1t
|
||||||
10063=Thiết bị đã được kích hoạt
|
10063=Thi\u1EBFt b\u1ECB \u0111\u00E3 \u0111\u01B0\u1EE3c k\u00EDch ho\u1EA1t
|
||||||
10064=Mô hình này là mô hình mặc định, vui lòng đặt mô hình khác làm mặc định trước
|
10064=M\u00F4 h\u00ECnh n\u00E0y l\u00E0 m\u00F4 h\u00ECnh m\u1EB7c \u0111\u1ECBnh, vui l\u00F2ng \u0111\u1EB7t m\u00F4 h\u00ECnh kh\u00E1c l\u00E0m m\u1EB7c \u0111\u1ECBnh tr\u01B0\u1EDBc
|
||||||
10065=Thêm dữ liệu thất bại
|
10065=Th\u00EAm d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||||
10066=Cập nhật dữ liệu thất bại
|
10066=C\u1EADp nh\u1EADt d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||||
10067=Lỗi mã xác minh đồ họa
|
10067=L\u1ED7i m\u00E3 x\u00E1c minh \u0111\u1ED3 h\u1ECDa
|
||||||
10068=Đăng ký di động không được bật, không thể sử dụng chức năng mã xác minh SMS
|
10068=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng m\u00E3 x\u00E1c minh SMS
|
||||||
10069=Tên người dùng không phải là số điện thoại di động, vui lòng nhập lại
|
10069=T\u00EAn ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng ph\u1EA3i l\u00E0 s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng, vui l\u00F2ng nh\u1EADp l\u1EA1i
|
||||||
10070=Số điện thoại di động này đã được đăng ký
|
10070=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
|
||||||
10071=Số điện thoại di động đã nhập không được đăng ký
|
10071=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
|
||||||
10072=Hiện không cho phép đăng ký người dùng thông thường
|
10072=Hi\u1EC7n kh\u00F4ng cho ph\u00E9p \u0111\u0103ng k\u00FD ng\u01B0\u1EDDi d\u00F9ng th\u00F4ng th\u01B0\u1EDDng
|
||||||
10073=Đăng ký di động không được bật, không thể sử dụng chức năng truy xuất mật khẩu
|
10073=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng truy xu\u1EA5t m\u1EADt kh\u1EA9u
|
||||||
10074=Định dạng số điện thoại di động đã nhập không chính xác
|
10074=\u0110\u1ECBnh d\u1EA1ng s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10075=Mã xác minh SMS đã nhập không chính xác
|
10075=M\u00E3 x\u00E1c minh SMS \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10076=Loại từ điển không tồn tại
|
10076=Lo\u1EA1i t\u1EEB \u0111i\u1EC3n kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10077=Mã loại từ điển bị trùng lặp
|
10077=M\u00E3 lo\u1EA1i t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
|
||||||
10078=Đọc tài nguyên thất bại
|
10078=\u0110\u1ECDc t\u00E0i nguy\u00EAn th\u1EA5t b\u1EA1i
|
||||||
10079=Mô hình LLM và nhận dạng ý định, lựa chọn tham số không khớp
|
10079=M\u00F4 h\u00ECnh LLM v\u00E0 nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh, l\u1EF1a ch\u1ECDn tham s\u1ED1 kh\u00F4ng kh\u1EDBp
|
||||||
10080=Dấu giọng nói này thuộc về {0} đã đăng ký, vui lòng chọn giọng nói khác
|
10080=D\u1EA5u gi\u1ECDng n\u00F3i n\u00E0y thu\u1ED9c v\u1EC1 {0} \u0111\u00E3 \u0111\u0103ng k\u00FD, vui l\u00F2ng ch\u1ECDn gi\u1ECDng n\u00F3i kh\u00E1c
|
||||||
10081=Đã xảy ra lỗi khi xóa dấu giọng nói
|
10081=\u0110\u00E3 x\u1EA3y ra l\u1ED7i khi x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i
|
||||||
10082=Không cho phép sửa đổi, giọng nói này đã được đăng ký làm dấu giọng nói ({0})
|
10082=Kh\u00F4ng cho ph\u00E9p s\u1EEDa \u0111\u1ED5i, gi\u1ECDng n\u00F3i n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD l\u00E0m d\u1EA5u gi\u1ECDng n\u00F3i ({0})
|
||||||
10083=Lỗi sửa đổi dấu giọng nói, vui lòng liên hệ quản trị viên
|
10083=L\u1ED7i s\u1EEDa \u0111\u1ED5i d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
|
||||||
10084=Lỗi địa chỉ giao diện dấu giọng nói, vui lòng vào quản lý tham số để sửa đổi địa chỉ giao diện dấu giọng nói
|
10084=L\u1ED7i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng v\u00E0o qu\u1EA3n l\u00FD tham s\u1ED1 \u0111\u1EC3 s\u1EEDa \u0111\u1ED5i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i
|
||||||
10085=Dữ liệu âm thanh không thuộc về agent này
|
10085=D\u1EEF li\u1EC7u \u00E2m thanh kh\u00F4ng thu\u1ED9c v\u1EC1 agent n\u00E0y
|
||||||
10086=Dữ liệu âm thanh trống, vui lòng kiểm tra dữ liệu đã tải lên
|
10086=D\u1EEF li\u1EC7u \u00E2m thanh tr\u1ED1ng, vui l\u00F2ng ki\u1EC3m tra d\u1EEF li\u1EC7u \u0111\u00E3 t\u1EA3i l\u00EAn
|
||||||
10087=Đăng ký dấu giọng nói thất bại, yêu cầu không thành công
|
10087=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
|
||||||
10088=Đăng ký dấu giọng nói thất bại, xử lý yêu cầu thất bại
|
10088=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||||
10089=Hủy dấu giọng nói thất bại, yêu cầu không thành công
|
10089=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
|
||||||
10090=Hủy dấu giọng nói thất bại, xử lý yêu cầu thất bại
|
10090=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||||
10091=Nhà cung cấp mô hình không tồn tại
|
10091=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10092=LLM được cấu hình không tồn tại
|
10092=LLM \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10093=Cấu hình mô hình này được tham chiếu bởi agent {0} và không thể xóa
|
10093=C\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi agent {0} v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
|
||||||
10094=Mô hình LLM này được tham chiếu bởi cấu hình nhận dạng ý định và không thể xóa
|
10094=M\u00F4 h\u00ECnh LLM n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi c\u1EA5u h\u00ECnh nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
|
||||||
10095=Thao tác máy chủ không hợp lệ
|
10095=Thao t\u00E1c m\u00E1y ch\u1EE7 kh\u00F4ng h\u1EE3p l\u1EC7
|
||||||
10096=Địa chỉ WebSocket máy chủ chưa được cấu hình
|
10096=\u0110\u1ECBa ch\u1EC9 WebSocket m\u00E1y ch\u1EE7 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
|
||||||
10097=Địa chỉ WebSocket mục tiêu không tồn tại
|
10097=\u0110\u1ECBa ch\u1EC9 WebSocket m\u1EE5c ti\u00EAu kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10098=Danh sách địa chỉ WebSocket không thể để trống
|
10098=Danh s\u00E1ch \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10099=Địa chỉ WebSocket không thể sử dụng localhost hoặc 127.0.0.1
|
10099=\u0110\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||||
10100=Định dạng địa chỉ WebSocket không chính xác
|
10100=\u0110\u1ECBnh d\u1EA1ng \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10101=Kiểm tra kết nối WebSocket thất bại
|
10101=Ki\u1EC3m tra k\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i
|
||||||
10102=Địa chỉ OTA không thể để trống
|
10102=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10103=Địa chỉ OTA không thể sử dụng localhost hoặc 127.0.0.1
|
10103=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||||
10104=Địa chỉ OTA phải bắt đầu bằng http hoặc https
|
10104=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||||
10105=Địa chỉ OTA phải kết thúc bằng /ota/
|
10105=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i k\u1EBFt th\u00FAc b\u1EB1ng /ota/
|
||||||
10106=Truy cập giao diện OTA thất bại
|
10106=Truy c\u1EADp giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
|
||||||
10107=Định dạng nội dung trả về giao diện OTA không chính xác
|
10107=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n OTA kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10108=Xác thực giao diện OTA thất bại
|
10108=X\u00E1c th\u1EF1c giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
|
||||||
10109=Địa chỉ MCP không thể để trống
|
10109=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10110=Địa chỉ MCP không thể sử dụng localhost hoặc 127.0.0.1
|
10110=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||||
10111=Không phải địa chỉ MCP hợp lệ
|
10111=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 MCP h\u1EE3p l\u1EC7
|
||||||
10112=Truy cập giao diện MCP thất bại
|
10112=Truy c\u1EADp giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
|
||||||
10113=Định dạng nội dung trả về giao diện MCP không chính xác
|
10113=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n MCP kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10114=Xác thực giao diện MCP thất bại
|
10114=X\u00E1c th\u1EF1c giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
|
||||||
10115=Địa chỉ giao diện dấu giọng nói không thể để trống
|
10115=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10116=Địa chỉ giao diện dấu giọng nói không thể sử dụng localhost hoặc 127.0.0.1
|
10116=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||||
10117=Không phải địa chỉ giao diện dấu giọng nói hợp lệ
|
10117=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i h\u1EE3p l\u1EC7
|
||||||
10118=Địa chỉ giao diện dấu giọng nói phải bắt đầu bằng http hoặc https
|
10118=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||||
10119=Truy cập giao diện dấu giọng nói thất bại
|
10119=Truy c\u1EADp giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
|
||||||
10120=Định dạng nội dung trả về giao diện dấu giọng nói không chính xác
|
10120=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||||
10121=Xác thực giao diện dấu giọng nói thất bại
|
10121=X\u00E1c th\u1EF1c giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
|
||||||
10122=Bí mật MQTT không thể để trống
|
10122=B\u00ED m\u1EADt MQTT kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10123=Bí mật MQTT của bạn không an toàn, cần ít nhất 8 ký tự và phải chứa cả chữ hoa và chữ thường
|
10123=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, c\u1EA7n \u00EDt nh\u1EA5t 8 k\u00FD t\u1EF1 v\u00E0 ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
|
||||||
10124=Bí mật MQTT của bạn không an toàn, bí mật MQTT phải chứa cả chữ hoa và chữ thường
|
10124=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, b\u00ED m\u1EADt MQTT ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
|
||||||
10125=Bí mật MQTT của bạn chứa mật khẩu yếu
|
10125=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n ch\u1EE9a m\u1EADt kh\u1EA9u y\u1EBFu
|
||||||
10128=Nhãn từ điển bị trùng lặp
|
10128=Nh\u00E3n t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
|
||||||
10129=Khóa SM2 chưa được cấu hình
|
10129=Kh\u00F3a SM2 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
|
||||||
10130=Giải mã SM2 thất bại
|
10130=Gi\u1EA3i m\u00E3 SM2 th\u1EA5t b\u1EA1i
|
||||||
10131=modelType và provideCode không thể để trống
|
10131=modelType v\u00E0 provideCode kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10132=Không có quyền xem lịch sử trò chuyện của agent này
|
10132=Kh\u00F4ng c\u00F3 quy\u1EC1n xem l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n c\u1EE7a agent n\u00E0y
|
||||||
10133=ID phiên không thể để trống
|
10133=ID phi\u00EAn kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10134=ID agent không thể để trống
|
10134=ID agent kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10135=Tải xuống lịch sử trò chuyện thất bại
|
10135=T\u1EA3i xu\u1ED1ng l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n th\u1EA5t b\u1EA1i
|
||||||
10136=Liên kết tải xuống đã hết hạn hoặc không hợp lệ
|
10136=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng \u0111\u00E3 h\u1EBFt h\u1EA1n ho\u1EB7c kh\u00F4ng h\u1EE3p l\u1EC7
|
||||||
10137=Liên kết tải xuống không hợp lệ
|
10137=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng kh\u00F4ng h\u1EE3p l\u1EC7
|
||||||
10138=Người dùng
|
10138=Ng\u01B0\u1EDDi d\u00F9ng
|
||||||
10139=Agent
|
10139=Agent
|
||||||
10140=Tệp âm thanh không thể để trống
|
10140=T\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10141=Chỉ hỗ trợ tệp âm thanh
|
10141=Ch\u1EC9 h\u1ED7 tr\u1EE3 t\u1EC7p \u00E2m thanh
|
||||||
10142=Kích thước tệp âm thanh không thể vượt quá 10MB
|
10142=K\u00EDch th\u01B0\u1EDBc t\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 v\u01B0\u1EE3t qu\u00E1 10MB
|
||||||
10143=Tải lên thất bại
|
10143=T\u1EA3i l\u00EAn th\u1EA5t b\u1EA1i
|
||||||
10144=Bản ghi nhân bản giọng nói không tồn tại
|
10144=B\u1EA3n ghi nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10145=Thông tin tài nguyên giọng nói không thể để trống
|
10145=Th\u00F4ng tin t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10146=Tên nền tảng TTS không thể để trống
|
10146=T\u00EAn n\u1EC1n t\u1EA3ng TTS kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10147=ID giọng nói không thể để trống
|
10147=ID gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10148=Tài khoản chủ sở hữu không thể để trống
|
10148=T\u00E0i kho\u1EA3n ch\u1EE7 s\u1EDF h\u1EEFu kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10149=ID tài nguyên giọng nói cần xóa không thể để trống
|
10149=ID t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i c\u1EA7n x\u00F3a kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10150=Bạn không có quyền thao tác bản ghi này
|
10150=B\u1EA1n kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c b\u1EA3n ghi n\u00E0y
|
||||||
10151=Vui lòng tải lên tệp âm thanh trước
|
10151=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u00E2m thanh tr\u01B0\u1EDBc
|
||||||
10152=Không tìm thấy cấu hình mô hình
|
10152=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh
|
||||||
10153=Không tìm thấy loại mô hình
|
10153=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i m\u00F4 h\u00ECnh
|
||||||
10154=Đào tạo thất bại: {0}
|
10154=\u0110\u00E0o t\u1EA1o th\u1EA5t b\u1EA1i: {0}
|
||||||
10155=Thiếu cấu hình Huoshan Engine
|
10155=Thi\u1EBFu c\u1EA5u h\u00ECnh Huoshan Engine
|
||||||
10156=Lỗi định dạng phản hồi, thiếu trường BaseResp
|
10156=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ph\u1EA3n h\u1ED3i, thi\u1EBFu tr\u01B0\u1EDDng BaseResp
|
||||||
10157=Yêu cầu thất bại
|
10157=Y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||||
10158=Nhân bản âm sắc:
|
10158=Nh\u00E2n b\u1EA3n \u00E2m s\u1EAFc:
|
||||||
10159=ID giọng nói đã tồn tại
|
10159=ID gi\u1ECDng n\u00F3i \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||||
10160=Lỗi định dạng ID giọng nói Huoshan Engine, phải bắt đầu bằng S_
|
10160=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ID gi\u1ECDng n\u00F3i Huoshan Engine, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng S_
|
||||||
10161=Địa chỉ Mac đã tồn tại
|
10161=\u0110\u1ECBa ch\u1EC9 Mac \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||||
10162=Nhà cung cấp mô hình không tồn tại
|
10162=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10163=Bản ghi cơ sở kiến thức không tồn tại
|
10163=B\u1EA3n ghi c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||||
10164=Không tìm thấy cấu hình RAG
|
10164=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG
|
||||||
10165=Lỗi loại cấu hình RAG
|
10165=L\u1ED7i lo\u1EA1i c\u1EA5u h\u00ECnh RAG
|
||||||
10166=Không tìm thấy cấu hình RAG mặc định
|
10166=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG m\u1EB7c \u0111\u1ECBnh
|
||||||
10167=Gọi API RAG thất bại: {0}
|
10167=G\u1ECDi API RAG th\u1EA5t b\u1EA1i: {0}
|
||||||
10168=Tải lên tệp thất bại
|
10168=T\u1EA3i l\u00EAn t\u1EC7p th\u1EA5t b\u1EA1i
|
||||||
10169=Không có quyền thao tác cơ sở kiến thức này
|
10169=Kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c n\u00E0y
|
||||||
10170=Tên cơ sở kiến thức đã tồn tại
|
10170=T\u00EAn c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||||
10171=Cấu hình RAG base_url không thể để trống
|
10171=C\u1EA5u h\u00ECnh RAG base_url kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10172=Cấu hình RAG api_key không thể để trống
|
10172=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10173=Cấu hình RAG api_key không thể chứa trình giữ chỗ, vui lòng thay thế bằng khóa API thực tế
|
10173=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 ch\u1EE9a tr\u00ECnh gi\u1EEF ch\u1ED7, vui l\u00F2ng thay th\u1EBF b\u1EB1ng kh\u00F3a API th\u1EF1c t\u1EBF
|
||||||
10174=Lỗi định dạng cấu hình RAG base_url, phải bắt đầu bằng http hoặc https
|
10174=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng c\u1EA5u h\u00ECnh RAG base_url, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||||
10175=Địa chỉ Mac không thể để trống
|
10175=\u0110\u1ECBa ch\u1EC9 Mac kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10176=Cấu hình RAG dataset_id không thể để trống
|
10176=C\u1EA5u h\u00ECnh RAG dataset_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10177=Cấu hình RAG model_id không thể để trống
|
10177=C\u1EA5u h\u00ECnh RAG model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10178=Cấu hình RAG dataset_id và model_id không thể để trống
|
10178=C\u1EA5u h\u00ECnh RAG dataset_id v\u00E0 model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10179=Tên tệp không thể để trống
|
10179=T\u00EAn t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10180=Nội dung tệp không thể để trống
|
10180=N\u1ED9i dung t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10181=Tên nhân bản giọng nói không thể để trống
|
10181=T\u00EAn nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||||
10182=Không tìm thấy âm thanh nhân bản giọng nói
|
10182=Kh\u00F4ng t\u00ECm th\u1EA5y \u00E2m thanh nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i
|
||||||
10183=Không tìm thấy mẫu agent mặc định
|
10183=Kh\u00F4ng t\u00ECm th\u1EA5y m\u1EABu agent m\u1EB7c \u0111\u1ECBnh
|
||||||
10184=Loại bộ chuyển đổi không được hỗ trợ
|
10184=Lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
||||||
10185=Kiểm tra cấu hình RAG thất bại
|
10185=Ki\u1EC3m tra c\u1EA5u h\u00ECnh RAG th\u1EA5t b\u1EA1i
|
||||||
10186=Tạo bộ chuyển đổi thất bại
|
10186=T\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||||
10187=Khởi tạo bộ chuyển đổi thất bại
|
10187=Kh\u1EDFi t\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||||
10188=Kiểm tra kết nối bộ chuyển đổi thất bại
|
10188=Ki\u1EC3m tra k\u1EBFt n\u1ED1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||||
10189=Thao tác bộ chuyển đổi thất bại
|
10189=Thao t\u00E1c b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||||
10190=Không tìm thấy bộ chuyển đổi
|
10190=Kh\u00F4ng t\u00ECm th\u1EA5y b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||||
10191=Lỗi bộ nhớ đệm bộ chuyển đổi
|
10191=L\u1ED7i b\u1ED9 nh\u1EDB \u0111\u1EC7m b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||||
10192=Không tìm thấy loại bộ chuyển đổi
|
10192=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||||
|
10193=ID thi\u1EBFt b\u1ECB kh\u00F4ng th\u1EC3 tr\u1ED1ng
|
||||||
|
10194=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB ho\u1EB7c thi\u1EBFt b\u1ECB kh\u00F4ng tr\u1EF1c tuy\u1EBFn
|
||||||
|
10195=S\u1ED1 l\u1EA7n t\u1EA3i l\u00EAn OTA v\u01B0\u1EE3t qu\u00E1 gi\u1EDBi h\u1EA1n
|
||||||
@@ -198,4 +198,7 @@
|
|||||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
||||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
||||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||||
|
10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
|
10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
|
||||||
|
10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11appid\u6216access_token
|
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11appid\u6216access_token
|
||||||
10156=\u97FF\u61C9\u683C\u5F0F\u932F\u8AA4\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
10156=\u97FF\u61C9\u683C\u5F0F\u932F\u8AA4\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
||||||
10157=\u8ACB\u6C42\u5931\u6557
|
10157=\u8ACB\u6C42\u5931\u6557
|
||||||
10158=\u514b\u9686\u97f3\u8272:
|
10158=\u514B\u9686\u97F3\u8272:
|
||||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||||
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u4EE5S_\u958B\u982D
|
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u4EE5S_\u958B\u982D
|
||||||
10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
|
10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
|
||||||
@@ -173,23 +173,23 @@
|
|||||||
10164=RAG\u914D\u7F6E\u672A\u627E\u5230
|
10164=RAG\u914D\u7F6E\u672A\u627E\u5230
|
||||||
10165=RAG\u914D\u7F6E\u985E\u578B\u932F\u8AA4
|
10165=RAG\u914D\u7F6E\u985E\u578B\u932F\u8AA4
|
||||||
10166=\u9810\u8A2DRAG\u914D\u7F6E\u672A\u627E\u5230
|
10166=\u9810\u8A2DRAG\u914D\u7F6E\u672A\u627E\u5230
|
||||||
10167=\u0052\u0041\u0047\u8abf\u7528\u5931\u6557\uFF0C{0}
|
10167=RAG\u8ABF\u7528\u5931\u6557\uFF0C{0}
|
||||||
10168=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557
|
10168=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557
|
||||||
10169=\u60A8\u6C92\u6709\u6B0A\u9650\u64CD\u4F5C\u8A72\u8A18\u9304
|
10169=\u60A8\u6C92\u6709\u6B0A\u9650\u64CD\u4F5C\u8A72\u8A18\u9304
|
||||||
10170=\u77E5\u8B58\u5EAB\u540D\u7A31\u91CD\u8907
|
10170=\u77E5\u8B58\u5EAB\u540D\u7A31\u91CD\u8907
|
||||||
10171=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
|
10171=RAG\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10172=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
|
10172=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10173=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
|
10173=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
|
||||||
10174=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
|
10174=RAG\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
|
||||||
10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
|
10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10176=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
|
10176=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10177=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
|
10177=RAG\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10178=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
|
10178=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
10179=\u6587\u4ef6\u540d\u7a31\u4e0d\u80fd\u70ba\u7a7a
|
10179=\u6587\u4EF6\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
|
||||||
10180=\u6587\u4ef6\u5185\u5bb9\u4e0d\u80fd\u70ba\u7a7a
|
10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u70BA\u7A7A
|
||||||
10181=\u97f3\u8272\u514b\u9686\u540d\u7a31\u4e0d\u80fd\u70ba\u7a7a
|
10181=\u97F3\u8272\u514B\u9686\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
|
||||||
10182=\u97f3\u8272\u514b\u9686\u97f3\u983b\u4e0d\u5b58\u5728
|
10182=\u97F3\u8272\u514B\u9686\u97F3\u983B\u4E0D\u5B58\u5728
|
||||||
10183=\u9ed8\u8ba4\u667a\u80fd\u4f53\u672a\u627e\u5230
|
10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
|
||||||
10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u985E\u578B
|
10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u985E\u578B
|
||||||
10185=RAG\u914D\u7F6E\u9A57\u8B49\u5931\u6557
|
10185=RAG\u914D\u7F6E\u9A57\u8B49\u5931\u6557
|
||||||
10186=\u9002\u914D\u5668\u5275\u5EFA\u5931\u6557
|
10186=\u9002\u914D\u5668\u5275\u5EFA\u5931\u6557
|
||||||
@@ -198,4 +198,7 @@
|
|||||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u6557
|
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u6557
|
||||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u932F\u8AA4
|
10191=\u9002\u914D\u5668\u7F13\u5B58\u932F\u8AA4
|
||||||
10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
|
10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
|
||||||
|
10193=\u8A2D\u5099ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
|
10194=\u8A2D\u5099\u4E0D\u5B58\u5728\u6216\u672A\u5728\u7DDA
|
||||||
|
10195=OTA\u4E0A\u4F20\u6B21\u6578\u8D85\u904E\u9650\u5236
|
||||||
@@ -22,13 +22,18 @@
|
|||||||
created_at, updated_at
|
created_at, updated_at
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<delete id="deleteAudioByAgentId">
|
<select id="getAudioIdsByAgentId" resultType="java.lang.String">
|
||||||
DELETE FROM ai_agent_chat_audio
|
SELECT DISTINCT audio_id
|
||||||
WHERE id IN (
|
FROM ai_agent_chat_history
|
||||||
SELECT audio_id
|
WHERE agent_id = #{agentId} AND audio_id IS NOT NULL
|
||||||
FROM ai_agent_chat_history
|
</select>
|
||||||
WHERE agent_id = #{agentId}
|
|
||||||
)
|
<delete id="deleteAudioByIds">
|
||||||
|
DELETE FROM ai_agent_chat_audio
|
||||||
|
WHERE id IN
|
||||||
|
<foreach collection="audioIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
<update id="deleteAudioIdByAgentId">
|
<update id="deleteAudioIdByAgentId">
|
||||||
|
|||||||
@@ -15,4 +15,23 @@
|
|||||||
FROM sys_dict_type
|
FROM sys_dict_type
|
||||||
WHERE id = #{dictTypeId}
|
WHERE id = #{dictTypeId}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="getDictTypesByIdList" resultType="java.lang.String">
|
||||||
|
SELECT
|
||||||
|
dict_type
|
||||||
|
FROM
|
||||||
|
sys_dict_type
|
||||||
|
WHERE
|
||||||
|
id IN (
|
||||||
|
SELECT
|
||||||
|
dict_type_id
|
||||||
|
FROM
|
||||||
|
sys_dict_data
|
||||||
|
WHERE
|
||||||
|
id IN
|
||||||
|
<foreach collection="dictDataIdList" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -235,7 +235,7 @@ function showAbout() {
|
|||||||
title: t('settings.aboutApp', { appName: import.meta.env.VITE_APP_TITLE }),
|
title: t('settings.aboutApp', { appName: import.meta.env.VITE_APP_TITLE }),
|
||||||
content: t('settings.aboutContent', {
|
content: t('settings.aboutContent', {
|
||||||
appName: import.meta.env.VITE_APP_TITLE,
|
appName: import.meta.env.VITE_APP_TITLE,
|
||||||
version: '0.8.10'
|
version: '0.9.1'
|
||||||
}),
|
}),
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
confirmText: t('common.confirm'),
|
confirmText: t('common.confirm'),
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# 语音盒子主题自定义
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
本目录包含从 [xiaozhi-assets-generator](https://github.com/xinnan-tech/xiaozhi-assets-generator) 项目打包的静态文件,用于语音盒子主题的在线自定义与生成。用户可以通过此工具配置唤醒词、字体、表情和聊天背景等元素,并导出为 `assets.bin` 文件。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
generator/
|
||||||
|
├── assets/ # 构建生成的资源文件
|
||||||
|
│ ├── ft_render-ByO_jG18.js
|
||||||
|
│ ├── index-CYcyz9xb.js
|
||||||
|
│ └── index-NXxBVrod.css
|
||||||
|
├── static/ # 静态资源目录
|
||||||
|
│ ├── charsets/ # 字符集文件
|
||||||
|
│ │ ├── deepseek.txt
|
||||||
|
│ │ ├── gb2312.txt
|
||||||
|
│ │ ├── latin1.txt
|
||||||
|
│ │ └── qwen18409.txt
|
||||||
|
│ ├── fonts/ # 字体资源
|
||||||
|
│ │ ├── font_noto_qwen_14_1.bin
|
||||||
|
│ │ ├── font_noto_qwen_16_4.bin
|
||||||
|
│ │ ├── font_noto_qwen_20_4.bin
|
||||||
|
│ │ ├── font_noto_qwen_30_4.bin
|
||||||
|
│ │ ├── font_puhui_deepseek_14_1.bin
|
||||||
|
│ │ ├── font_puhui_deepseek_16_4.bin
|
||||||
|
│ │ ├── font_puhui_deepseek_20_4.bin
|
||||||
|
│ │ ├── font_puhui_deepseek_30_4.bin
|
||||||
|
│ │ ├── noto_qwen.ttf
|
||||||
|
│ │ └── puhui_deepseek.ttf
|
||||||
|
│ ├── multinet_model/ # 自定义唤醒词模型
|
||||||
|
│ │ ├── fst/
|
||||||
|
│ │ ├── mn6_cn/
|
||||||
|
│ │ ├── mn6_en/
|
||||||
|
│ │ ├── mn7_cn/
|
||||||
|
│ │ └── mn7_en/
|
||||||
|
│ ├── twemoji32/ # 32x32 表情图片
|
||||||
|
│ ├── twemoji64/ # 64x64 表情图片
|
||||||
|
│ ├── wakenet_model/ # 预设唤醒词模型
|
||||||
|
│ └── README.md # 静态资源说明
|
||||||
|
├── index.html # 主页面
|
||||||
|
└── README.md # 项目说明文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
### 1. 芯片与屏幕配置
|
||||||
|
- 支持多种芯片型号:ESP32-S3、ESP32-C3、ESP32-P4、ESP32-C6
|
||||||
|
- 灵活的屏幕分辨率设置
|
||||||
|
- 支持 RGB565 颜色格式
|
||||||
|
|
||||||
|
### 2. 唤醒词配置
|
||||||
|
- **预设唤醒词**:基于不同芯片支持的 WakeNet 模型
|
||||||
|
- **自定义唤醒词**:支持中文和英文命令词,可配置阈值和超时时间
|
||||||
|
|
||||||
|
### 3. 字体配置
|
||||||
|
- 预设多种字体:阿里巴巴普惠体、Noto Qwen 等
|
||||||
|
- 支持上传自定义 TTF/WOFF 字体文件
|
||||||
|
- 可配置字号和颜色深度(bpp)
|
||||||
|
|
||||||
|
### 4. 表情集合
|
||||||
|
- 提供 21 种基础表情的预设方案(32x32 和 64x64 两种尺寸)
|
||||||
|
- 支持自定义表情上传
|
||||||
|
|
||||||
|
### 5. 聊天背景
|
||||||
|
- 支持浅色/深色模式切换
|
||||||
|
- 可配置纯色背景或图片背景
|
||||||
|
- 自动适配屏幕分辨率
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 以服务方式启动 `index.html` 文件
|
||||||
|
2. 选择芯片型号和屏幕配置
|
||||||
|
3. 通过不同标签页配置主题元素
|
||||||
|
4. 点击生成按钮查看资源清单
|
||||||
|
5. 确认后生成并下载 `assets.bin` 文件
|
||||||
|
|
||||||
|
## 技术说明
|
||||||
|
|
||||||
|
- 构建后的静态资源位于 `assets/` 目录
|
||||||
|
- 原始模型和资源文件位于 `static/` 目录
|
||||||
|
- 支持离线使用,无需额外依赖
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 本工具为离线使用设计,所有资源已包含在目录中
|
||||||
|
- 生成的 `assets.bin` 文件需要与语音盒子硬件配合使用
|
||||||
|
- 自定义资源需注意文件格式和大小限制,以确保兼容
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" href="./favicon.ico">
|
||||||
|
<title>Xiaozhi AI Customization</title>
|
||||||
|
<script type="module" crossorigin src="./assets/index-B8r0c7xg.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="./assets/index-CrIJdTCK.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
### Alibaba Puhui Fonts
|
||||||
|
|
||||||
|
https://www.alibabafonts.com/
|
||||||
|
|
||||||
|
### twemoji32 / twemoji64
|
||||||
|
|
||||||
|
https://github.com/twitter/twemoji
|
||||||
|
|
||||||
|
### Wakenet models
|
||||||
|
|
||||||
|
The wakenet model weights are copied from the ESP-SR repo.
|
||||||
|
|
||||||
|
https://github.com/espressif/esp-sr/tree/master/model/wakenet_model
|
||||||
|
|
||||||
|
### Multinet models
|
||||||
|
|
||||||
|
The multinet model weights are copied from the ESP-SR repo.
|
||||||
|
|
||||||
|
https://github.com/espressif/esp-sr/tree/master/model/multinet_model
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,190 @@
|
|||||||
|
|
||||||
|
!
|
||||||
|
"
|
||||||
|
#
|
||||||
|
$
|
||||||
|
%
|
||||||
|
&
|
||||||
|
'
|
||||||
|
(
|
||||||
|
)
|
||||||
|
*
|
||||||
|
+
|
||||||
|
,
|
||||||
|
-
|
||||||
|
.
|
||||||
|
/
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
:
|
||||||
|
;
|
||||||
|
<
|
||||||
|
=
|
||||||
|
>
|
||||||
|
?
|
||||||
|
@
|
||||||
|
A
|
||||||
|
B
|
||||||
|
C
|
||||||
|
D
|
||||||
|
E
|
||||||
|
F
|
||||||
|
G
|
||||||
|
H
|
||||||
|
I
|
||||||
|
J
|
||||||
|
K
|
||||||
|
L
|
||||||
|
M
|
||||||
|
N
|
||||||
|
O
|
||||||
|
P
|
||||||
|
Q
|
||||||
|
R
|
||||||
|
S
|
||||||
|
T
|
||||||
|
U
|
||||||
|
V
|
||||||
|
W
|
||||||
|
X
|
||||||
|
Y
|
||||||
|
Z
|
||||||
|
[
|
||||||
|
\
|
||||||
|
]
|
||||||
|
^
|
||||||
|
_
|
||||||
|
`
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
e
|
||||||
|
f
|
||||||
|
g
|
||||||
|
h
|
||||||
|
i
|
||||||
|
j
|
||||||
|
k
|
||||||
|
l
|
||||||
|
m
|
||||||
|
n
|
||||||
|
o
|
||||||
|
p
|
||||||
|
q
|
||||||
|
r
|
||||||
|
s
|
||||||
|
t
|
||||||
|
u
|
||||||
|
v
|
||||||
|
w
|
||||||
|
x
|
||||||
|
y
|
||||||
|
z
|
||||||
|
{
|
||||||
|
|
|
||||||
|
}
|
||||||
|
~
|
||||||
|
¡
|
||||||
|
¢
|
||||||
|
£
|
||||||
|
¤
|
||||||
|
¥
|
||||||
|
¦
|
||||||
|
§
|
||||||
|
¨
|
||||||
|
©
|
||||||
|
ª
|
||||||
|
«
|
||||||
|
¬
|
||||||
|
|
||||||
|
®
|
||||||
|
¯
|
||||||
|
°
|
||||||
|
±
|
||||||
|
²
|
||||||
|
³
|
||||||
|
´
|
||||||
|
µ
|
||||||
|
¶
|
||||||
|
·
|
||||||
|
¸
|
||||||
|
¹
|
||||||
|
º
|
||||||
|
»
|
||||||
|
¼
|
||||||
|
½
|
||||||
|
¾
|
||||||
|
¿
|
||||||
|
À
|
||||||
|
Á
|
||||||
|
Â
|
||||||
|
Ã
|
||||||
|
Ä
|
||||||
|
Å
|
||||||
|
Æ
|
||||||
|
Ç
|
||||||
|
È
|
||||||
|
É
|
||||||
|
Ê
|
||||||
|
Ë
|
||||||
|
Ì
|
||||||
|
Í
|
||||||
|
Î
|
||||||
|
Ï
|
||||||
|
Ð
|
||||||
|
Ñ
|
||||||
|
Ò
|
||||||
|
Ó
|
||||||
|
Ô
|
||||||
|
Õ
|
||||||
|
Ö
|
||||||
|
×
|
||||||
|
Ø
|
||||||
|
Ù
|
||||||
|
Ú
|
||||||
|
Û
|
||||||
|
Ü
|
||||||
|
Ý
|
||||||
|
Þ
|
||||||
|
ß
|
||||||
|
à
|
||||||
|
á
|
||||||
|
â
|
||||||
|
ã
|
||||||
|
ä
|
||||||
|
å
|
||||||
|
æ
|
||||||
|
ç
|
||||||
|
è
|
||||||
|
é
|
||||||
|
ê
|
||||||
|
ë
|
||||||
|
ì
|
||||||
|
í
|
||||||
|
î
|
||||||
|
ï
|
||||||
|
ð
|
||||||
|
ñ
|
||||||
|
ò
|
||||||
|
ó
|
||||||
|
ô
|
||||||
|
õ
|
||||||
|
ö
|
||||||
|
÷
|
||||||
|
ø
|
||||||
|
ù
|
||||||
|
ú
|
||||||
|
û
|
||||||
|
ü
|
||||||
|
ý
|
||||||
|
þ
|
||||||
|
ÿ
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,313 @@
|
|||||||
|
1,ba xiao shi hou guan ji
|
||||||
|
2,ba xiao shi hou kai ji
|
||||||
|
3,bi kai wo chui
|
||||||
|
4,chao qiang feng su
|
||||||
|
5,chao zhe wo chui
|
||||||
|
6,chou shi mo shi
|
||||||
|
7,chu shi mo shi
|
||||||
|
8,da kai bai feng
|
||||||
|
9,da kai bing xiang
|
||||||
|
10,da kai chou shi mo shi
|
||||||
|
11,da kai chu jun
|
||||||
|
12,da kai chu shi mo shi
|
||||||
|
13,da kai dian fu re
|
||||||
|
14,da kai ding chu feng
|
||||||
|
15,da kai ding xiang chu wu
|
||||||
|
16,da kai er tong fang leng feng
|
||||||
|
17,da kai fang zhi chui
|
||||||
|
18,da kai feng ji
|
||||||
|
19,da kai fu re
|
||||||
|
20,da kai gan zao
|
||||||
|
21,da kai huan ji tiao wen
|
||||||
|
22,da kai jia shi
|
||||||
|
23,da kai jing hua
|
||||||
|
24,da kai jun yun feng
|
||||||
|
25,da kai kong tiao
|
||||||
|
26,da kai kong tiao deng guang
|
||||||
|
27,da kai mu ying feng
|
||||||
|
28,da kai mu qing feng
|
||||||
|
29,da kai pai feng mo shi
|
||||||
|
30,da kai pai qi
|
||||||
|
31,da kai pai qi mo shi
|
||||||
|
32,da kai qiang jing
|
||||||
|
33,da kai quan wu feng gan
|
||||||
|
34,da kai rou feng gan
|
||||||
|
35,da kai shang wu feng gan
|
||||||
|
36,da kai shang xia bai feng
|
||||||
|
37,da kai shang xia sao feng
|
||||||
|
38,da kai sen ba
|
||||||
|
39,da kai shu sheng
|
||||||
|
40,da kai shu shi feng
|
||||||
|
41,da kai shui mian
|
||||||
|
42,da kai si ji chu shi
|
||||||
|
43,da kai si ji chu shi mo shi
|
||||||
|
44,da kai song feng
|
||||||
|
45,da kai song feng mo shi
|
||||||
|
46,da kai wu feng gan
|
||||||
|
47,da kai xia wu feng gan
|
||||||
|
48,da kai xin feng
|
||||||
|
49,da kai xiu xian mo shi
|
||||||
|
50,da kai xuan zhuan song feng
|
||||||
|
51,da kai yi jian chu wu
|
||||||
|
52,da kai zhi leng
|
||||||
|
53,da kai zhi leng mo shi
|
||||||
|
54,da kai zhi re
|
||||||
|
55,da kai zhi re mo shi
|
||||||
|
56,da kai zhi kong wen
|
||||||
|
57,da kai zhi neng guan jia
|
||||||
|
58,da kai zhi neng sha jun
|
||||||
|
59,da kai zhi neng sheng dian
|
||||||
|
60,da kai zhi qing jie
|
||||||
|
61,da kai zhi wen gan
|
||||||
|
62,da kai zhuan shu wen du
|
||||||
|
63,da kai zi dong mo shi
|
||||||
|
64,da kai zuo you bai feng
|
||||||
|
65,da kai zuo you sao feng
|
||||||
|
66,di feng su
|
||||||
|
67,di su feng
|
||||||
|
68,di yi dian
|
||||||
|
69,dian fu re
|
||||||
|
70,tiao cheng zui leng
|
||||||
|
71,tiao cheng zui nuan
|
||||||
|
72,tiao da feng su
|
||||||
|
73,tiao da xin feng
|
||||||
|
74,tiao dao er shi ba du
|
||||||
|
75,tiao dao er shi du
|
||||||
|
76,tiao dao er shi er du
|
||||||
|
77,tiao dao er shi jiu du
|
||||||
|
78,tiao dao er shi liu du
|
||||||
|
79,tiao dao er shi qi du
|
||||||
|
80,tiao dao er shi san du
|
||||||
|
81,tiao dao er shi si du
|
||||||
|
82,tiao dao er shi wu du
|
||||||
|
83,tiao dao er shi yi du
|
||||||
|
84,tiao dao san shi du
|
||||||
|
85,tiao dao shi ba du
|
||||||
|
86,tiao dao shi jiu du
|
||||||
|
87,tiao dao shi liu du
|
||||||
|
88,tiao dao shi qi du
|
||||||
|
89,tiao dao zui leng
|
||||||
|
90,tiao dao zui nuan
|
||||||
|
91,tiao di ling dian wu du
|
||||||
|
92,tiao di wen du
|
||||||
|
93,tiao di yi du
|
||||||
|
94,tiao gao er shi du
|
||||||
|
95,tiao gao ling dian wu du
|
||||||
|
96,tiao gao wen du
|
||||||
|
97,tiao gao wu du
|
||||||
|
98,tiao gao yi du
|
||||||
|
99,tiao leng yi dian
|
||||||
|
100,tiao nuan
|
||||||
|
101,tiao nuan yi dian
|
||||||
|
102,tiao xiao feng su
|
||||||
|
103,tiao xiao xin feng
|
||||||
|
104,ding chu feng
|
||||||
|
105,ding shi wu rao
|
||||||
|
106,ding xiang chu wu
|
||||||
|
107,er tong fang leng feng
|
||||||
|
108,er dang xin feng
|
||||||
|
109,feng da yi dian
|
||||||
|
110,feng su da dian
|
||||||
|
111,feng su er dang
|
||||||
|
112,feng su san dang
|
||||||
|
113,feng su xiao dian
|
||||||
|
114,feng su yi dang
|
||||||
|
115,feng wang shang chui
|
||||||
|
116,feng wang xia chui
|
||||||
|
117,feng wang you chui
|
||||||
|
118,feng wang zhong jian chui
|
||||||
|
119,feng wang zuo chui
|
||||||
|
120,feng xiang shang chui
|
||||||
|
121,feng xiang xia chui
|
||||||
|
122,feng xiang you chui
|
||||||
|
123,feng xiang zhong jian chui
|
||||||
|
124,feng xiang zuo chui
|
||||||
|
125,feng xiao yi dian
|
||||||
|
126,gao feng su
|
||||||
|
127,gao su feng
|
||||||
|
128,guan bi bai feng
|
||||||
|
129,fang zhi chui
|
||||||
|
130,guan bi dian fu re
|
||||||
|
131,guan bi ding chu feng
|
||||||
|
132,guan bi ding xiang chu wu
|
||||||
|
133,guan bi er tong fang leng feng
|
||||||
|
134,guan bi fang zhi chui
|
||||||
|
135,guan bi feng ji
|
||||||
|
136,guan bi fu re
|
||||||
|
137,guan bi huan qi
|
||||||
|
138,guan bi huan qi mo shi
|
||||||
|
139,guan bi jia shi
|
||||||
|
140,guan bi jie neng
|
||||||
|
141,guan bi jun yun feng
|
||||||
|
142,guan bi kong tiao
|
||||||
|
143,guan bi kong tiao deng guang
|
||||||
|
144,guan bi mu ying feng
|
||||||
|
145,guan bi mu qing feng
|
||||||
|
146,guan bi qiang jing
|
||||||
|
147,guan bi quan wu feng gan
|
||||||
|
148,guan bi rou feng gan
|
||||||
|
149,guan bi sen ba
|
||||||
|
150,guan bi shang wu feng gan
|
||||||
|
151,guan bi shang xia bai feng
|
||||||
|
152,guan bi shang xia sao feng
|
||||||
|
153,guan bi shu sheng
|
||||||
|
154,guan bi shui mian
|
||||||
|
155,guan bi si ji chu shi
|
||||||
|
156,guan bi si ji chu shi mo shi
|
||||||
|
157,guan bi wu feng gan
|
||||||
|
158,guan bi xia wu feng gan
|
||||||
|
159,guan bi xin feng
|
||||||
|
160,guan bi xuan zhuan song feng
|
||||||
|
161,guan bi yi jian chu wu
|
||||||
|
162,guan bi zhi kong wen
|
||||||
|
163,guan bi zhi qing jie
|
||||||
|
164,guan bi zhong wen bao wen
|
||||||
|
165,guan bi zhuan xiang wen du er
|
||||||
|
166,guan bi zhuan xiang wen du san
|
||||||
|
167,guan bi zhuan xiang wen du yi
|
||||||
|
168,guan bi zuo you bai feng
|
||||||
|
169,guan bi zuo you sao feng
|
||||||
|
170,guan bi zhuan shu wen du
|
||||||
|
171,guan diao bai feng
|
||||||
|
172,guan diao dian fu re
|
||||||
|
173,guan diao ding chu feng
|
||||||
|
174,guan diao ding xiang chu wu
|
||||||
|
175,guan diao er tong fang leng feng
|
||||||
|
176,guan diao fang zhi chui
|
||||||
|
177,guan diao gan zao
|
||||||
|
178,guan diao huan qi
|
||||||
|
179,guan diao huan qi mo shi
|
||||||
|
180,guan diao jia shi
|
||||||
|
181,guan diao jing hua
|
||||||
|
182,guan diao jun yun feng
|
||||||
|
183,guan diao kong tiao
|
||||||
|
184,guan diao kong tiao deng guang
|
||||||
|
185,guan diao qiang jing
|
||||||
|
186,guan diao quan wu feng gan
|
||||||
|
187,guan diao rou feng gan
|
||||||
|
188,guan diao shang wu feng gan
|
||||||
|
189,guan diao shang xia bai feng
|
||||||
|
190,guan diao shu sheng
|
||||||
|
191,guan diao shui mian
|
||||||
|
192,guan diao si ji chu shi
|
||||||
|
193,guan diao si ji chu shi mo shi
|
||||||
|
194,guan diao wu feng gan
|
||||||
|
195,guan diao xia wu feng gan
|
||||||
|
196,guan diao xin feng
|
||||||
|
197,guan diao xuan zhuan song feng
|
||||||
|
198,guan diao yi jian chu wu
|
||||||
|
199,guan diao zhi kong wen
|
||||||
|
200,guan diao zhi qing jie
|
||||||
|
201,guan diao zuo you bai feng
|
||||||
|
202,guan kong tiao
|
||||||
|
203,guan shang xia bai feng
|
||||||
|
204,guan shang xia feng
|
||||||
|
205,guan xin feng
|
||||||
|
206,guan zuo you bai feng
|
||||||
|
207,guan zuo you feng
|
||||||
|
208,guan bi zhi neng sheng dian
|
||||||
|
209,jian xiao feng su
|
||||||
|
210,jian xiao pu tong xin feng
|
||||||
|
211,jian xiao xin feng
|
||||||
|
212,jiang di wen du
|
||||||
|
213,jing yin feng
|
||||||
|
214,jing yin feng su
|
||||||
|
215,jun yun feng
|
||||||
|
216,kai kong tiao
|
||||||
|
217,kai qi bai feng
|
||||||
|
218,kai qi chu shi
|
||||||
|
219,kai qi chu shi mo shi
|
||||||
|
220,kai qi kong tiao
|
||||||
|
221,kai qi pai feng mo shi
|
||||||
|
222,kai qi shang xia sao feng
|
||||||
|
223,kai qi tong feng
|
||||||
|
224,kai qi zhi leng
|
||||||
|
225,kai qi zhi leng mo shi
|
||||||
|
226,kai qi zhi re
|
||||||
|
227,kai qi zhi re mo shi
|
||||||
|
228,kai qi zuo you sao feng
|
||||||
|
229,kai shi chu wei
|
||||||
|
230,kong tiao da kai
|
||||||
|
231,kong tiao guan bi
|
||||||
|
232,kong tiao guan diao
|
||||||
|
233,kong tiao guan ji
|
||||||
|
234,kong tiao kai ji
|
||||||
|
235,kong tiao kai shi pei wang
|
||||||
|
236,kong tiao lian wang
|
||||||
|
237,kong tiao chong xin pei wang
|
||||||
|
238,kai xin feng
|
||||||
|
239,leng yi dian
|
||||||
|
240,nuan yi dian
|
||||||
|
241,pu tong xin feng
|
||||||
|
242,qi dong kong tiao
|
||||||
|
243,qiang jing xin feng
|
||||||
|
244,qiang li feng
|
||||||
|
245,qiang li feng su
|
||||||
|
246,qu shi mo shi
|
||||||
|
247,quan wu feng gan
|
||||||
|
248,rou feng gan
|
||||||
|
249,san dang xin feng
|
||||||
|
250,shang wu feng gan
|
||||||
|
251,shang xia bai feng
|
||||||
|
252,shang xia feng
|
||||||
|
253,sheng gao wen du
|
||||||
|
254,sheng dao zui gao
|
||||||
|
255,sheng dao zui gao wei zhi
|
||||||
|
256,si dang xin feng
|
||||||
|
257,si ji chu shi
|
||||||
|
258,si ji chu shi mo shi
|
||||||
|
259,song feng mo shi
|
||||||
|
260,tai leng le
|
||||||
|
261,tai re le
|
||||||
|
262,ting zhi shang xia bai feng
|
||||||
|
263,ting zhi shang xia sao feng
|
||||||
|
264,ting zhi bai feng
|
||||||
|
265,ting zhi zhuan shu wen du
|
||||||
|
266,ting zhi zuo you bai feng
|
||||||
|
267,ting zhi zuo you sao feng
|
||||||
|
268,xia wu feng gan
|
||||||
|
269,xin feng da dian
|
||||||
|
270,xin feng er dang
|
||||||
|
271,xin feng qiang jing
|
||||||
|
272,xin feng qiang jing dang
|
||||||
|
273,xin feng san dang
|
||||||
|
274,xin feng si dang
|
||||||
|
275,xin feng xiao dian
|
||||||
|
276,xin feng yi dang
|
||||||
|
277,xin feng zui da
|
||||||
|
278,xin feng zui da dang
|
||||||
|
279,xin feng zui xiao
|
||||||
|
280,xin feng zui xiao dang
|
||||||
|
281,xuan zhuan song feng
|
||||||
|
282,yi dang xin feng
|
||||||
|
283,you dian leng
|
||||||
|
284,you dian re
|
||||||
|
285,zai gao yi dian
|
||||||
|
286,zeng da feng su
|
||||||
|
287,zeng da xin feng
|
||||||
|
288,zhi leng mo shi
|
||||||
|
289,zhi re mo shi
|
||||||
|
290,zhi kong wen
|
||||||
|
291,zhi neng wu rao
|
||||||
|
292,zhi qing jie
|
||||||
|
293,zhong deng feng
|
||||||
|
294,zhong feng su
|
||||||
|
295,zhong su feng
|
||||||
|
296,zi dong
|
||||||
|
297,zi dong feng
|
||||||
|
298,zi dong feng su
|
||||||
|
299,zi dong mo shi
|
||||||
|
300,zui da feng
|
||||||
|
301,zui da feng su
|
||||||
|
302,zui da xin feng
|
||||||
|
303,zui xiao feng
|
||||||
|
304,zui xiao feng su
|
||||||
|
305,zui xiao xin feng
|
||||||
|
306,zuo you bai feng
|
||||||
|
307,zuo you feng
|
||||||
|
308,bang wo guan deng
|
||||||
|
309,bang wo kai deng
|
||||||
|
310,da kai dian deng
|
||||||
|
311,guan bi dian deng
|
||||||
|
312,xiao le xiao le
|
||||||
|
313,xiao xin xiao xin
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
1,TELL ME A JOKE,TfL Mm c qbK
|
||||||
|
2,SING A SONG,Sgl c Sel
|
||||||
|
3,PLAY NEWS CHANNEL,PLd NoZ paNcL
|
||||||
|
4,TURN ON MY SOUNDBOX,TkN nN Mi StNDBnKS
|
||||||
|
5,TURN OFF MY SOUNDBOX,TkN eF Mi StNDBnKS
|
||||||
|
5,TURN OF MY SOUNDBOX,TkN cV Mi StNDBnKS
|
||||||
|
6,HIGHEST VOLUME,hicST VnLYoM
|
||||||
|
7,LOWEST VOLUME,LbcST VnLYoM
|
||||||
|
8,INCREASE THE VOLUME,gNKRmS jc VnLYoM
|
||||||
|
9,DECREASE THE VOLUME,DgKRmS jc VnLYoM
|
||||||
|
10,TURN ON THE TV,TkN nN jc TmVm
|
||||||
|
11,TURN OFF THE TV,TkN eF jc TmVm
|
||||||
|
11,TURN OF THE TV,TkN cV jc TmVm
|
||||||
|
12,MAKE ME A TEA,MdK Mm c Tm
|
||||||
|
13,MAKE ME A COFFEE,MdK Mm c KnFm
|
||||||
|
14,TURN ON THE LIGHT,TkN nN jc LiT
|
||||||
|
15,TURN OFF THE LIGHT,TkN eF jc LiT
|
||||||
|
15,TURN OF THE LIGHT,TkN cV jc LiT
|
||||||
|
16,CHANGE THE COLOR TO RED,pdNq jc KcLk To RfD
|
||||||
|
17,CHANGE THE COLOR TO GREEN,pdNq jc KcLk To GRmN
|
||||||
|
18,TURN ON ALL THE LIGHTS,TkN nN eL jc LiTS
|
||||||
|
19,TURN OFF ALL THE LIGHTS,TkN eF eL jc LiTS
|
||||||
|
19,TURN OF ALL THE LIGHTS,TkN cV eL jc LiTS
|
||||||
|
20,TURN ON THE AIR CONDITIONER,TkN nN jc fR KcNDgscNk
|
||||||
|
21,TURN OFF THE AIR CONDITIONER,TkN eF jc fR KcNDgscNk
|
||||||
|
21,TURN OF THE AIR CONDITIONER,TkN cV jc fR KcNDgscNk
|
||||||
|
22,SET THE TEMPERATURE TO SIXTEEN DEGREES,SfT jc TfMPRcpk To SgKSTmN DgGRmZ
|
||||||
|
23,SET THE TEMPERATURE TO SEVENTEEN DEGREES,SfT jc TfMPRcpk To SfVcNTmN DgGRmZ
|
||||||
|
24,SET THE TEMPERATURE TO EIGHTEEN DEGREES,SfT jc TfMPRcpk To dTmN DgGRmZ
|
||||||
|
25,SET THE TEMPERATURE TO NINETEEN DEGREES,SfT jc TfMPRcpk To NiNTmN DgGRmZ
|
||||||
|
26,SET THE TEMPERATURE TO TWENTY DEGREES,SfT jc TfMPRcpk To TWfNTm DgGRmZ
|
||||||
|
27,SET THE TEMPERATURE TO TWENTY ONE DEGREES,SfT jc TfMPRcpk To TWfNTm WcN DgGRmZ
|
||||||
|
28,SET THE TEMPERATURE TO TWENTY TWO DEGREES,SfT jc TfMPRcpk To TWfNTm To DgGRmZ
|
||||||
|
29,SET THE TEMPERATURE TO TWENTY THREE DEGREES,SfT jc TfMPRcpk To TWfNTm vRm DgGRmZ
|
||||||
|
30,SET THE TEMPERATURE TO TWENTY FOUR DEGREES,SfT jc TfMPRcpk To TWfNTm FeR DgGRmZ
|
||||||
|
31,SET THE TEMPERATURE TO TWENTY FIVE DEGREES,SfT jc TfMPRcpk To TWfNTm FiV DgGRmZ
|
||||||
|
32,SET THE TEMPERATURE TO TWENTY SIX DEGREES,SfT jc TfMPRcpk To TWfNTm SgKS DgGRmZ
|
||||||
|
33,LOWEST FAN SPEED,LbcST FaN SPmD
|
||||||
|
34,MEDIUM FAN SPEED,MmDmcM FaN SPmD
|
||||||
|
35,HIGHEST FAN SPEED,hicST FaN SPmD
|
||||||
|
36,AUTO ADJUST THE FAN SPEED,eTb cqcST jc FaN SPmD
|
||||||
|
37,DECREASE THE FAN SPEED,DgKRmS jc FaN SPmD
|
||||||
|
38,INCREASE THE FAN SPEED,gNKRmS jc FaN SPmD
|
||||||
|
39,INCREASE THE TEMPERATURE,gNKRmS jc TfMPRcpk
|
||||||
|
40,DECREASE THE TEMPERATURE,DgKRmS jc TfMPRcpk
|
||||||
|
41,COOLING MODE,KoLgl MbD
|
||||||
|
42,HEATING MODE,hmTgl MbD
|
||||||
|
43,VENTILATION MODE,VfNTcLdscN MbD
|
||||||
|
44,DEHUMIDIFY MODE,DmhYoMgDcFi MbD
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# (neural network type)_(model data version)_(lable1_detection windown length_threshold for 90%_threshold for 95%)_(lable2 ...)_...
|
||||||
|
MN6_v3_chinese_8_0.9_0.90
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,408 @@
|
|||||||
|
<blk> 0
|
||||||
|
<sos/eos> 0
|
||||||
|
<unk> 0
|
||||||
|
▁a -6.00389
|
||||||
|
▁ai -6.00389
|
||||||
|
▁an -6.00389
|
||||||
|
▁ang -6.00389
|
||||||
|
▁ao -6.00389
|
||||||
|
▁ba -6.00389
|
||||||
|
▁bai -6.00389
|
||||||
|
▁ban -6.00389
|
||||||
|
▁bang -6.00389
|
||||||
|
▁bao -6.00389
|
||||||
|
▁bei -6.00389
|
||||||
|
▁ben -6.00389
|
||||||
|
▁beng -6.00389
|
||||||
|
▁bi -6.00389
|
||||||
|
▁bian -6.00389
|
||||||
|
▁biao -6.00389
|
||||||
|
▁bie -6.00389
|
||||||
|
▁bin -6.00389
|
||||||
|
▁bing -6.00389
|
||||||
|
▁bo -6.00389
|
||||||
|
▁bu -6.00389
|
||||||
|
▁ca -6.00389
|
||||||
|
▁cai -6.00389
|
||||||
|
▁can -6.00389
|
||||||
|
▁cang -6.00389
|
||||||
|
▁cao -6.00389
|
||||||
|
▁ce -6.00389
|
||||||
|
▁cen -6.00389
|
||||||
|
▁ceng -6.00389
|
||||||
|
▁cha -6.00389
|
||||||
|
▁chai -6.00389
|
||||||
|
▁chan -6.00389
|
||||||
|
▁chang -6.00389
|
||||||
|
▁chao -6.00389
|
||||||
|
▁che -6.00389
|
||||||
|
▁chen -6.00389
|
||||||
|
▁cheng -6.00389
|
||||||
|
▁chi -6.00389
|
||||||
|
▁chong -6.00389
|
||||||
|
▁chou -6.00389
|
||||||
|
▁chu -6.00389
|
||||||
|
▁chuai -6.00389
|
||||||
|
▁chuan -6.00389
|
||||||
|
▁chuang -6.00389
|
||||||
|
▁chui -6.00389
|
||||||
|
▁chun -6.00389
|
||||||
|
▁chuo -6.00389
|
||||||
|
▁ci -6.00389
|
||||||
|
▁cong -6.00389
|
||||||
|
▁cou -6.00389
|
||||||
|
▁cu -6.00389
|
||||||
|
▁cuan -6.00389
|
||||||
|
▁cui -6.00389
|
||||||
|
▁cun -6.00389
|
||||||
|
▁cuo -6.00389
|
||||||
|
▁da -6.00389
|
||||||
|
▁dai -6.00389
|
||||||
|
▁dan -6.00389
|
||||||
|
▁dang -6.00389
|
||||||
|
▁dao -6.00389
|
||||||
|
▁de -6.00389
|
||||||
|
▁dei -6.00389
|
||||||
|
▁deng -6.00389
|
||||||
|
▁di -6.00389
|
||||||
|
▁dia -6.00389
|
||||||
|
▁dian -6.00389
|
||||||
|
▁diao -6.00389
|
||||||
|
▁die -6.00389
|
||||||
|
▁ding -6.00389
|
||||||
|
▁diu -6.00389
|
||||||
|
▁dong -6.00389
|
||||||
|
▁dou -6.00389
|
||||||
|
▁du -6.00389
|
||||||
|
▁duan -6.00389
|
||||||
|
▁dui -6.00389
|
||||||
|
▁dun -6.00389
|
||||||
|
▁duo -6.00389
|
||||||
|
▁e -6.00389
|
||||||
|
▁ei -6.00389
|
||||||
|
▁en -6.00389
|
||||||
|
▁eng -6.00389
|
||||||
|
▁er -6.00389
|
||||||
|
▁fa -6.00389
|
||||||
|
▁fan -6.00389
|
||||||
|
▁fang -6.00389
|
||||||
|
▁fei -6.00389
|
||||||
|
▁fen -6.00389
|
||||||
|
▁feng -6.00389
|
||||||
|
▁fo -6.00389
|
||||||
|
▁fou -6.00389
|
||||||
|
▁fu -6.00389
|
||||||
|
▁ga -6.00389
|
||||||
|
▁gai -6.00389
|
||||||
|
▁gan -6.00389
|
||||||
|
▁gang -6.00389
|
||||||
|
▁gao -6.00389
|
||||||
|
▁ge -6.00389
|
||||||
|
▁gei -6.00389
|
||||||
|
▁gen -6.00389
|
||||||
|
▁geng -6.00389
|
||||||
|
▁gong -6.00389
|
||||||
|
▁gou -6.00389
|
||||||
|
▁gu -6.00389
|
||||||
|
▁gua -6.00389
|
||||||
|
▁guai -6.00389
|
||||||
|
▁guan -6.00389
|
||||||
|
▁guang -6.00389
|
||||||
|
▁gui -6.00389
|
||||||
|
▁gun -6.00389
|
||||||
|
▁guo -6.00389
|
||||||
|
▁ha -6.00389
|
||||||
|
▁hai -6.00389
|
||||||
|
▁han -6.00389
|
||||||
|
▁hang -6.00389
|
||||||
|
▁hao -6.00389
|
||||||
|
▁he -6.00389
|
||||||
|
▁hei -6.00389
|
||||||
|
▁hen -6.00389
|
||||||
|
▁heng -6.00389
|
||||||
|
▁hong -6.00389
|
||||||
|
▁hou -6.00389
|
||||||
|
▁hu -6.00389
|
||||||
|
▁hua -6.00389
|
||||||
|
▁huai -6.00389
|
||||||
|
▁huan -6.00389
|
||||||
|
▁huang -6.00389
|
||||||
|
▁hui -6.00389
|
||||||
|
▁hun -6.00389
|
||||||
|
▁huo -6.00389
|
||||||
|
▁ji -6.00389
|
||||||
|
▁jia -6.00389
|
||||||
|
▁jian -6.00389
|
||||||
|
▁jiang -6.00389
|
||||||
|
▁jiao -6.00389
|
||||||
|
▁jie -6.00389
|
||||||
|
▁jin -6.00389
|
||||||
|
▁jing -6.00389
|
||||||
|
▁jiong -6.00389
|
||||||
|
▁jiu -6.00389
|
||||||
|
▁ju -6.00389
|
||||||
|
▁juan -6.00389
|
||||||
|
▁jue -6.00389
|
||||||
|
▁jun -6.00389
|
||||||
|
▁ka -6.00389
|
||||||
|
▁kai -6.00389
|
||||||
|
▁kan -6.00389
|
||||||
|
▁kang -6.00389
|
||||||
|
▁kao -6.00389
|
||||||
|
▁ke -6.00389
|
||||||
|
▁ken -6.00389
|
||||||
|
▁keng -6.00389
|
||||||
|
▁kong -6.00389
|
||||||
|
▁kou -6.00389
|
||||||
|
▁ku -6.00389
|
||||||
|
▁kua -6.00389
|
||||||
|
▁kuai -6.00389
|
||||||
|
▁kuan -6.00389
|
||||||
|
▁kuang -6.00389
|
||||||
|
▁kui -6.00389
|
||||||
|
▁kun -6.00389
|
||||||
|
▁kuo -6.00389
|
||||||
|
▁la -6.00389
|
||||||
|
▁lai -6.00389
|
||||||
|
▁lan -6.00389
|
||||||
|
▁lang -6.00389
|
||||||
|
▁lao -6.00389
|
||||||
|
▁le -6.00389
|
||||||
|
▁lei -6.00389
|
||||||
|
▁leng -6.00389
|
||||||
|
▁li -6.00389
|
||||||
|
▁lia -6.00389
|
||||||
|
▁lian -6.00389
|
||||||
|
▁liang -6.00389
|
||||||
|
▁liao -6.00389
|
||||||
|
▁lie -6.00389
|
||||||
|
▁lin -6.00389
|
||||||
|
▁ling -6.00389
|
||||||
|
▁liu -6.00389
|
||||||
|
▁long -6.00389
|
||||||
|
▁lou -6.00389
|
||||||
|
▁lu -6.00389
|
||||||
|
▁luan -6.00389
|
||||||
|
▁lun -6.00389
|
||||||
|
▁luo -6.00389
|
||||||
|
▁lv -6.00389
|
||||||
|
▁lve -6.00389
|
||||||
|
▁ma -6.00389
|
||||||
|
▁mai -6.00389
|
||||||
|
▁man -6.00389
|
||||||
|
▁mang -6.00389
|
||||||
|
▁mao -6.00389
|
||||||
|
▁me -6.00389
|
||||||
|
▁mei -6.00389
|
||||||
|
▁men -6.00389
|
||||||
|
▁meng -6.00389
|
||||||
|
▁mi -6.00389
|
||||||
|
▁mian -6.00389
|
||||||
|
▁miao -6.00389
|
||||||
|
▁mie -6.00389
|
||||||
|
▁min -6.00389
|
||||||
|
▁ming -6.00389
|
||||||
|
▁miu -6.00389
|
||||||
|
▁mo -6.00389
|
||||||
|
▁mou -6.00389
|
||||||
|
▁mu -6.00389
|
||||||
|
▁n -6.00389
|
||||||
|
▁na -6.00389
|
||||||
|
▁nai -6.00389
|
||||||
|
▁nan -6.00389
|
||||||
|
▁nang -6.00389
|
||||||
|
▁nao -6.00389
|
||||||
|
▁ne -6.00389
|
||||||
|
▁nei -6.00389
|
||||||
|
▁nen -6.00389
|
||||||
|
▁neng -6.00389
|
||||||
|
▁ni -6.00389
|
||||||
|
▁nian -6.00389
|
||||||
|
▁niang -6.00389
|
||||||
|
▁niao -6.00389
|
||||||
|
▁nie -6.00389
|
||||||
|
▁nin -6.00389
|
||||||
|
▁ning -6.00389
|
||||||
|
▁niu -6.00389
|
||||||
|
▁nong -6.00389
|
||||||
|
▁nou -6.00389
|
||||||
|
▁nu -6.00389
|
||||||
|
▁nuan -6.00389
|
||||||
|
▁nuo -6.00389
|
||||||
|
▁nv -6.00389
|
||||||
|
▁nve -6.00389
|
||||||
|
▁o -6.00389
|
||||||
|
▁ou -6.00389
|
||||||
|
▁pa -6.00389
|
||||||
|
▁pai -6.00389
|
||||||
|
▁pan -6.00389
|
||||||
|
▁pang -6.00389
|
||||||
|
▁pao -6.00389
|
||||||
|
▁pei -6.00389
|
||||||
|
▁pen -6.00389
|
||||||
|
▁peng -6.00389
|
||||||
|
▁pi -6.00389
|
||||||
|
▁pian -6.00389
|
||||||
|
▁piao -6.00389
|
||||||
|
▁pie -6.00389
|
||||||
|
▁pin -6.00389
|
||||||
|
▁ping -6.00389
|
||||||
|
▁po -6.00389
|
||||||
|
▁pou -6.00389
|
||||||
|
▁pu -6.00389
|
||||||
|
▁qi -6.00389
|
||||||
|
▁qia -6.00389
|
||||||
|
▁qian -6.00389
|
||||||
|
▁qiang -6.00389
|
||||||
|
▁qiao -6.00389
|
||||||
|
▁qie -6.00389
|
||||||
|
▁qin -6.00389
|
||||||
|
▁qing -6.00389
|
||||||
|
▁qiong -6.00389
|
||||||
|
▁qiu -6.00389
|
||||||
|
▁qu -6.00389
|
||||||
|
▁quan -6.00389
|
||||||
|
▁que -6.00389
|
||||||
|
▁qun -6.00389
|
||||||
|
▁ran -6.00389
|
||||||
|
▁rang -6.00389
|
||||||
|
▁rao -6.00389
|
||||||
|
▁re -6.00389
|
||||||
|
▁ren -6.00389
|
||||||
|
▁reng -6.00389
|
||||||
|
▁ri -6.00389
|
||||||
|
▁rong -6.00389
|
||||||
|
▁rou -6.00389
|
||||||
|
▁ru -6.00389
|
||||||
|
▁ruan -6.00389
|
||||||
|
▁rui -6.00389
|
||||||
|
▁run -6.00389
|
||||||
|
▁ruo -6.00389
|
||||||
|
▁sa -6.00389
|
||||||
|
▁sai -6.00389
|
||||||
|
▁san -6.00389
|
||||||
|
▁sang -6.00389
|
||||||
|
▁sao -6.00389
|
||||||
|
▁se -6.00389
|
||||||
|
▁sen -6.00389
|
||||||
|
▁seng -6.00389
|
||||||
|
▁sha -6.00389
|
||||||
|
▁shai -6.00389
|
||||||
|
▁shan -6.00389
|
||||||
|
▁shang -6.00389
|
||||||
|
▁shao -6.00389
|
||||||
|
▁she -6.00389
|
||||||
|
▁shei -6.00389
|
||||||
|
▁shen -6.00389
|
||||||
|
▁sheng -6.00389
|
||||||
|
▁shi -6.00389
|
||||||
|
▁shou -6.00389
|
||||||
|
▁shu -6.00389
|
||||||
|
▁shua -6.00389
|
||||||
|
▁shuai -6.00389
|
||||||
|
▁shuan -6.00389
|
||||||
|
▁shuang -6.00389
|
||||||
|
▁shui -6.00389
|
||||||
|
▁shun -6.00389
|
||||||
|
▁shuo -6.00389
|
||||||
|
▁si -6.00389
|
||||||
|
▁song -6.00389
|
||||||
|
▁sou -6.00389
|
||||||
|
▁su -6.00389
|
||||||
|
▁suan -6.00389
|
||||||
|
▁sui -6.00389
|
||||||
|
▁sun -6.00389
|
||||||
|
▁suo -6.00389
|
||||||
|
▁ta -6.00389
|
||||||
|
▁tai -6.00389
|
||||||
|
▁tan -6.00389
|
||||||
|
▁tang -6.00389
|
||||||
|
▁tao -6.00389
|
||||||
|
▁te -6.00389
|
||||||
|
▁teng -6.00389
|
||||||
|
▁ti -6.00389
|
||||||
|
▁tian -6.00389
|
||||||
|
▁tiao -6.00389
|
||||||
|
▁tie -6.00389
|
||||||
|
▁ting -6.00389
|
||||||
|
▁tong -6.00389
|
||||||
|
▁tou -6.00389
|
||||||
|
▁tu -6.00389
|
||||||
|
▁tuan -6.00389
|
||||||
|
▁tui -6.00389
|
||||||
|
▁tun -6.00389
|
||||||
|
▁tuo -6.00389
|
||||||
|
▁wa -6.00389
|
||||||
|
▁wai -6.00389
|
||||||
|
▁wan -6.00389
|
||||||
|
▁wang -6.00389
|
||||||
|
▁wei -6.00389
|
||||||
|
▁wen -6.00389
|
||||||
|
▁weng -6.00389
|
||||||
|
▁wo -6.00389
|
||||||
|
▁wu -6.00389
|
||||||
|
▁xi -6.00389
|
||||||
|
▁xia -6.00389
|
||||||
|
▁xian -6.00389
|
||||||
|
▁xiang -6.00389
|
||||||
|
▁xiao -6.00389
|
||||||
|
▁xie -6.00389
|
||||||
|
▁xin -6.00389
|
||||||
|
▁xing -6.00389
|
||||||
|
▁xiong -6.00389
|
||||||
|
▁xiu -6.00389
|
||||||
|
▁xu -6.00389
|
||||||
|
▁xuan -6.00389
|
||||||
|
▁xue -6.00389
|
||||||
|
▁xun -6.00389
|
||||||
|
▁ya -6.00389
|
||||||
|
▁yan -6.00389
|
||||||
|
▁yang -6.00389
|
||||||
|
▁yao -6.00389
|
||||||
|
▁ye -6.00389
|
||||||
|
▁yi -6.00389
|
||||||
|
▁yin -6.00389
|
||||||
|
▁ying -6.00389
|
||||||
|
▁yo -6.00389
|
||||||
|
▁yong -6.00389
|
||||||
|
▁you -6.00389
|
||||||
|
▁yu -6.00389
|
||||||
|
▁yuan -6.00389
|
||||||
|
▁yue -6.00389
|
||||||
|
▁yun -6.00389
|
||||||
|
▁za -6.00389
|
||||||
|
▁zai -6.00389
|
||||||
|
▁zan -6.00389
|
||||||
|
▁zang -6.00389
|
||||||
|
▁zao -6.00389
|
||||||
|
▁ze -6.00389
|
||||||
|
▁zei -6.00389
|
||||||
|
▁zen -6.00389
|
||||||
|
▁zeng -6.00389
|
||||||
|
▁zha -6.00389
|
||||||
|
▁zhai -6.00389
|
||||||
|
▁zhan -6.00389
|
||||||
|
▁zhang -6.00389
|
||||||
|
▁zhao -6.00389
|
||||||
|
▁zhe -6.00389
|
||||||
|
▁zhen -6.00389
|
||||||
|
▁zheng -6.00389
|
||||||
|
▁zhi -6.00389
|
||||||
|
▁zhong -6.00389
|
||||||
|
▁zhou -6.00389
|
||||||
|
▁zhu -6.00389
|
||||||
|
▁zhua -6.00389
|
||||||
|
▁zhuai -6.00389
|
||||||
|
▁zhuan -6.00389
|
||||||
|
▁zhuang -6.00389
|
||||||
|
▁zhui -6.00389
|
||||||
|
▁zhun -6.00389
|
||||||
|
▁zhuo -6.00389
|
||||||
|
▁zi -6.00389
|
||||||
|
▁zong -6.00389
|
||||||
|
▁zou -6.00389
|
||||||
|
▁zu -6.00389
|
||||||
|
▁zuan -6.00389
|
||||||
|
▁zui -6.00389
|
||||||
|
▁zun -6.00389
|
||||||
|
▁zuo -6.00389
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# (neural network type)_(model data version)_(lable1_detection windown length_threshold for 90%_threshold for 95%)_(lable2 ...)_...
|
||||||
|
MN6_v11_english_8_0.9_0.90
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user