mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge branch 'main' into update-tts-voice-data
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
<a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
|
||||
<a href="./README_vi.md"><img alt="Tiếng Việt" src="https://img.shields.io/badge/Tiếng Việt-DFE0E5"></a>
|
||||
<a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
|
||||
<a href="./README_pt_BR.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-DFE0E5"></a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
|
||||
<img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
|
||||
</a>
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@ Unterstützt MQTT+UDP-Protokoll, Websocket-Protokoll, MCP-Endpunkte und Stimmabd
|
||||
<a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
|
||||
<a href="./README_vi.md"><img alt="Tiếng Việt" src="https://img.shields.io/badge/Tiếng Việt-DFE0E5"></a>
|
||||
<a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DBEDFA"></a>
|
||||
<a href="./README_pt_BR.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-DFE0E5"></a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
|
||||
<img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
|
||||
</a>
|
||||
@@ -258,7 +259,7 @@ Wenn Sie ein Softwareentwickler sind, finden Sie hier einen [Offenen Brief an En
|
||||
---
|
||||
|
||||
## 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#related-open-source-projects) 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/blob/main/README_zh.md#%E7%9B%B8%E5%85%B3%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE) in diesem Ökosystem ansehen
|
||||
|
||||
---
|
||||
|
||||
|
||||
+14
-14
@@ -21,6 +21,7 @@ Support for MQTT+UDP protocol, Websocket protocol, MCP access point, voiceprint
|
||||
<a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DBEDFA"></a>
|
||||
<a href="./README_vi.md"><img alt="Tiếng Việt" src="https://img.shields.io/badge/Tiếng Việt-DFE0E5"></a>
|
||||
<a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
|
||||
<a href="./README_pt_BR.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-DFE0E5"></a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
|
||||
<img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
|
||||
</a>
|
||||
@@ -182,8 +183,9 @@ This project provides two deployment methods. Please choose based on your specif
|
||||
| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
|
||||
|---------|------|---------|---------|---------|---------|
|
||||
| **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, 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) |
|
||||
| **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) |
|
||||
|
||||
For frequently asked questions and related tutorials, please refer to [this link](./docs/FAQ.md)
|
||||
|
||||
> 💡 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.
|
||||
|
||||
@@ -215,14 +217,15 @@ Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
||||
| 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) |
|
||||
|
||||
If you are concerned about the latency of each component, please refer to the [Xiaozhi Component Performance Test Report](https://github.com/xinnan-tech/xiaozhi-performance-research), and test in your own environment following the test methods in the report.
|
||||
|
||||
#### 🔧 Testing Tools
|
||||
This project provides the following testing tools to help you verify the system and choose suitable models:
|
||||
|
||||
| Tool Name | Location | Usage Method | Function Description |
|
||||
|:---:|:---|:---:|:---:|
|
||||
| Audio Interaction Test Tool | main》xiaozhi-server》test》test_page.html | Open directly with Google Chrome | Tests audio playback and reception functions, verifies if Python-side audio processing is normal |
|
||||
| Model Response Test Tool 1 | main》xiaozhi-server》performance_tester.py | Execute `python performance_tester.py` | Tests response speed of three core modules: ASR(speech recognition), LLM(large model), TTS(speech synthesis) |
|
||||
| Model Response Test Tool 2 | main》xiaozhi-server》performance_tester_vllm.py | Execute `python performance_tester_vllm.py` | Tests VLLM(vision model) response speed |
|
||||
| Model Response Test Tool | main》xiaozhi-server》performance_tester.py | Execute `python performance_tester.py` | Tests response speed of three core modules: ASR(speech recognition), LLM(large model), VLLM(vision model), TTS(speech synthesis) |
|
||||
|
||||
> 💡 Note: When testing model speed, only models with configured keys will be tested.
|
||||
|
||||
@@ -240,8 +243,8 @@ This project provides the following testing tools to help you verify the system
|
||||
| Intent Recognition | Supports LLM intent recognition, Function Call function calling, provides plugin-based intent processing mechanism |
|
||||
| Memory System | Supports local short-term memory, mem0ai interface memory, PowerMem intelligent memory, with memory summarization functionality |
|
||||
| Knowledge Base | Supports RAGFlow knowledge base, enabling LLM to judge whether to schedule the knowledge base after receiving the user's question, and then answer the question |
|
||||
| Command Delivery | Supports MCP command delivery to ESP32 devices via MQTT protocol from Smart Console |
|
||||
| Tool Calling | Supports client IOT protocol, client MCP protocol, server MCP protocol, MCP endpoint protocol, custom tool functions |
|
||||
| Command Delivery | Supports MCP command delivery to ESP32 devices via MQTT protocol from Smart Console |
|
||||
| Management Backend | Provides Web management interface, supports user management, system configuration and device management; Supports Simplified Chinese, Traditional Chinese and English display |
|
||||
| Testing Tools | Provides performance testing tools, vision model testing tools, and audio interaction testing tools |
|
||||
| Deployment Support | Supports Docker deployment and local deployment, provides complete configuration file management |
|
||||
@@ -249,20 +252,14 @@ This project provides the following testing tools to help you verify the system
|
||||
|
||||
### Under Development 🚧
|
||||
|
||||
To learn about specific development plan progress, [click here](https://github.com/users/xinnan-tech/projects/3)
|
||||
To learn about specific development plan progress, [click here](https://github.com/users/xinnan-tech/projects/3). For frequently asked questions and related tutorials, please refer to [this link](./docs/FAQ.md)
|
||||
|
||||
If you are a software developer, here is an [Open Letter to Developers](docs/contributor_open_letter.md). Welcome to join!
|
||||
|
||||
---
|
||||
|
||||
## Product Ecosystem 👬
|
||||
Xiaozhi is an ecosystem. When using this product, you 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 |
|
||||
|:---------------------|:--------|:--------|
|
||||
| Xiaozhi Android Client | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | An Android and iOS voice dialogue application based on xiaozhi-server, supporting real-time voice interaction and text dialogue.<br/>Currently a Flutter version, connecting iOS and Android platforms. |
|
||||
| Xiaozhi Desktop Client | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | This project provides a Python-based AI client for beginners, allowing users to experience Xiaozhi AI functionality through code even without physical hardware conditions. |
|
||||
| Xiaozhi Java Server | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | Xiaozhi open-source backend service Java version is a Java-based open-source project.<br/>It includes frontend and backend services, aiming to provide users with a complete backend service solution. |
|
||||
Xiaozhi is an ecosystem. When using this product, you can also check out other [excellent projects](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) in this ecosystem
|
||||
|
||||
---
|
||||
|
||||
@@ -276,8 +273,10 @@ Xiaozhi is an ecosystem. When using this product, you can also check out other [
|
||||
| Dify interface calls | Dify | - |
|
||||
| FastGPT interface calls | FastGPT | - |
|
||||
| Coze interface calls | Coze | - |
|
||||
| Xinference interface calls | Xinference | - |
|
||||
| HomeAssistant interface calls | HomeAssistant | - |
|
||||
|
||||
In fact, any LLM that supports OpenAI interface calls can be integrated and used, including Xinference and HomeAssistant interfaces.
|
||||
In fact, any LLM that supports OpenAI interface calls can be integrated and used.
|
||||
|
||||
---
|
||||
|
||||
@@ -296,7 +295,7 @@ In fact, any VLLM that supports OpenAI interface calls can be integrated and use
|
||||
| Usage Method | Supported Platforms | Free Platforms |
|
||||
|:---:|:---:|:---:|
|
||||
| Interface calls | EdgeTTS, iFLYTEK, Volcano Engine, Tencent Cloud, Alibaba Cloud and Bailian, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS, MinimaxTTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(partial) |
|
||||
| Local services | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS |
|
||||
| Local services | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
|
||||
|
||||
---
|
||||
|
||||
@@ -342,6 +341,7 @@ In fact, any VLLM that supports OpenAI interface calls can be integrated and use
|
||||
|:------:|:-------------:|:----:|:-------:|:---------------------:|
|
||||
| Intent | intent_llm | Interface calls | Based on LLM pricing | Recognizes intent through large models, strong generalization |
|
||||
| Intent | function_call | Interface calls | Based on LLM pricing | Completes intent through large model function calling, fast speed, good effect |
|
||||
| Intent | nointent | No intent mode | Free | Does not perform intent recognition, directly returns dialogue result |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
[](https://github.com/xinnan-tech/xiaozhi-esp32-server)
|
||||
|
||||
<h1 align="center">Serviço Backend Xiaozhi xiaozhi-esp32-server</h1>
|
||||
|
||||
<p align="center">
|
||||
Este projeto é baseado na teoria e tecnologia de inteligência simbiótica humano-máquina para desenvolver sistemas inteligentes de hardware e software para terminais<br/>fornecendo serviços de backend para o projeto de hardware inteligente de código aberto
|
||||
<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
|
||||
Implementado usando Python, Java e Vue de acordo com o <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Protocolo de Comunicação Xiaozhi</a><br/>
|
||||
Suporte ao protocolo MQTT+UDP, protocolo WebSocket, ponto de acesso MCP, reconhecimento de impressão vocal e base de conhecimento
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="./docs/FAQ.md">Perguntas Frequentes</a>
|
||||
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Reportar Problemas</a>
|
||||
· <a href="./README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Documentação de Implantação</a>
|
||||
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Notas de Lançamento</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="./README.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-DFE0E5"></a>
|
||||
<a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
|
||||
<a href="./README_vi.md"><img alt="Tiếng Việt" src="https://img.shields.io/badge/Tiếng Việt-DFE0E5"></a>
|
||||
<a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
|
||||
<a href="./README_pt_BR.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-DBEDFA"></a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
|
||||
<img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
|
||||
</a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/LICENSE">
|
||||
<img alt="GitHub pull requests" src="https://img.shields.io/badge/license-MIT-white?labelColor=black" />
|
||||
</a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server">
|
||||
<img alt="stars" src="https://img.shields.io/github/stars/xinnan-tech/xiaozhi-esp32-server?color=ffcb47&labelColor=black" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Liderado pela Equipe do Professor Siyuan Liu (Universidade de Tecnologia do Sul da China)
|
||||
</br>
|
||||
刘思源教授团队主导研发(华南理工大学)
|
||||
</br>
|
||||
<img src="./docs/images/hnlg.jpg" alt="Universidade de Tecnologia do Sul da China (华南理工大学)" width="50%">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Público-Alvo 👥
|
||||
|
||||
Este projeto requer dispositivos de hardware ESP32 para funcionar. Se você adquiriu hardware relacionado ao ESP32, conectou-se com sucesso ao serviço backend implantado pelo Brother Xia e deseja construir seu próprio serviço backend `xiaozhi-esp32` de forma independente, então este projeto é perfeito para você.
|
||||
|
||||
Quer ver os efeitos de uso? Clique nos vídeos abaixo 🎥
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
|
||||
<picture>
|
||||
<img alt="Experiência de velocidade de resposta" src="docs/images/demo9.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
|
||||
<picture>
|
||||
<img alt="Segredo da otimização de velocidade" src="docs/images/demo6.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
|
||||
<picture>
|
||||
<img alt="Cenário médico complexo" src="docs/images/demo1.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1zUW5zJEkq" target="_blank">
|
||||
<picture>
|
||||
<img alt="Envio de comandos MQTT" src="docs/images/demo4.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
|
||||
<picture>
|
||||
<img alt="Reconhecimento de impressão vocal" src="docs/images/demo14.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
|
||||
<picture>
|
||||
<img alt="Controle de interruptores de eletrodomésticos" src="docs/images/demo5.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
|
||||
<picture>
|
||||
<img alt="Ponto de acesso MCP" src="docs/images/demo13.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1TJ7WzzEo6" target="_blank">
|
||||
<picture>
|
||||
<img alt="Tarefas com múltiplos comandos" src="docs/images/demo11.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
|
||||
<picture>
|
||||
<img alt="Reproduzir música" src="docs/images/demo7.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1Z8XuYZEAS" target="_blank">
|
||||
<picture>
|
||||
<img alt="Plugin de clima" src="docs/images/demo8.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV12J7WzBEaH" target="_blank">
|
||||
<picture>
|
||||
<img alt="Interrupção em tempo real" src="docs/images/demo10.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
|
||||
<picture>
|
||||
<img alt="Fotografar e identificar objetos" src="docs/images/demo12.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
|
||||
<picture>
|
||||
<img alt="Timbre de voz personalizado" src="docs/images/demo2.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
|
||||
<picture>
|
||||
<img alt="Comunicação em cantonês" src="docs/images/demo3.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.bilibili.com/video/BV17LXWYvENb" target="_blank">
|
||||
<picture>
|
||||
<img alt="Transmissão de notícias" src="docs/images/demo0.png" />
|
||||
</picture>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## Avisos ⚠️
|
||||
|
||||
1. Este projeto é um software de código aberto. Este software não possui parceria comercial com nenhum provedor de serviços de API de terceiros (incluindo, mas não se limitando a reconhecimento de fala, modelos de linguagem, síntese de voz e outras plataformas) com os quais se conecta, e não fornece nenhuma forma de garantia quanto à qualidade de serviço ou segurança financeira desses provedores. Recomenda-se que os usuários priorizem provedores de serviço com licenças comerciais relevantes e leiam cuidadosamente seus termos de serviço e políticas de privacidade. Este software não armazena nenhuma chave de conta, não participa de fluxos de fundos e não assume o risco de perda de fundos recarregados.
|
||||
|
||||
2. A funcionalidade deste projeto não está completa e não passou por avaliação de segurança de rede. Por favor, não o utilize em ambientes de produção. Se você implantar este projeto para fins de aprendizado em um ambiente de rede pública, certifique-se de que as medidas de proteção necessárias estejam em vigor.
|
||||
|
||||
---
|
||||
|
||||
## Documentação de Implantação
|
||||
|
||||

|
||||
|
||||
Este projeto oferece dois métodos de implantação. Por favor, escolha de acordo com suas necessidades específicas:
|
||||
|
||||
#### 🚀 Seleção do Método de Implantação
|
||||
| Método de Implantação | Funcionalidades | Cenários Aplicáveis | Documentação de Implantação | Requisitos de Configuração | Tutoriais em Vídeo |
|
||||
|---------|------|---------|---------|---------|---------|
|
||||
| **Instalação Simplificada** | Diálogo inteligente, gerenciamento de agente único | Ambientes de baixa configuração, dados armazenados em arquivos de configuração, sem necessidade de banco de dados | [①Versão Docker](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②Implantação via Código-Fonte](./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 núcleos 4GB se usar `FunASR`, 2 núcleos 2GB se todas APIs | - |
|
||||
| **Instalação de Módulo Completo** | Diálogo inteligente, gerenciamento multiusuário, gerenciamento de múltiplos agentes, operação de interface do console inteligente | Experiência com funcionalidade completa, dados armazenados em banco de dados |[①Versão 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) / [②Implantação via Código-Fonte](./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) / [③Tutorial de Atualização Automática via Código-Fonte](./docs/dev-ops-integration.md) | 4 núcleos 8GB se usar `FunASR`, 2 núcleos 4GB se todas APIs| [Tutorial em Vídeo de Inicialização via Código-Fonte Local](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
|
||||
|
||||
Perguntas frequentes e tutoriais relacionados podem ser consultados [neste link](./docs/FAQ.md)
|
||||
|
||||
> 💡 Nota: Abaixo está uma plataforma de teste implantada com o código mais recente. Você pode gravar e testar se necessário. Usuários simultâneos: 6, os dados serão limpos diariamente.
|
||||
|
||||
```
|
||||
Endereço do Console de Controle Inteligente: https://2662r3426b.vicp.fun
|
||||
Endereço do Console de Controle Inteligente (H5): https://2662r3426b.vicp.fun/h5/index.html
|
||||
|
||||
Ferramenta de Teste de Serviço: https://2662r3426b.vicp.fun/test/
|
||||
Endereço da Interface OTA: https://2662r3426b.vicp.fun/xiaozhi/ota/
|
||||
Endereço da Interface WebSocket: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|
||||
```
|
||||
|
||||
#### 🚩 Descrição e Recomendações de Configuração
|
||||
> [!Note]
|
||||
> Este projeto oferece dois esquemas de configuração:
|
||||
>
|
||||
> 1. `Configurações Gratuitas Nível Básico`: Adequado para uso pessoal e doméstico, todos os componentes utilizam soluções gratuitas, sem necessidade de pagamento adicional.
|
||||
>
|
||||
> 2. `Configuração de Streaming`: Adequado para demonstrações, treinamentos, cenários com mais de 2 usuários simultâneos, etc. Utiliza tecnologia de processamento em streaming para velocidade de resposta mais rápida e melhor experiência.
|
||||
>
|
||||
> A partir da versão `0.5.2`, o projeto suporta configuração de streaming. Em comparação com versões anteriores, a velocidade de resposta é melhorada em aproximadamente `2,5 segundos`, melhorando significativamente a experiência do usuário.
|
||||
|
||||
| Nome do Módulo | Configurações Gratuitas Nível Básico | Configuração de Streaming |
|
||||
|:---:|:---:|:---:|
|
||||
| ASR(Reconhecimento de Fala) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
|
||||
| LLM(Modelo de Linguagem) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
|
||||
| VLLM(Modelo de Visão) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
|
||||
| TTS(Síntese de Voz) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
|
||||
| Intent(Reconhecimento de Intenção) | function_call(Chamada de função) | function_call(Chamada de função) |
|
||||
| Memory(Função de Memória) | mem_local_short(Memória local de curto prazo) | mem_local_short(Memória local de curto prazo) |
|
||||
|
||||
Se você está preocupado com o tempo de resposta de cada componente, consulte o [Relatório de Teste de Desempenho dos Componentes Xiaozhi](https://github.com/xinnan-tech/xiaozhi-performance-research), e teste em seu próprio ambiente seguindo os métodos de teste do relatório.
|
||||
|
||||
#### 🔧 Ferramentas de Teste
|
||||
Este projeto fornece as seguintes ferramentas de teste para ajudá-lo a verificar o sistema e escolher modelos adequados:
|
||||
|
||||
| Nome da Ferramenta | Localização | Método de Uso | Descrição da Função |
|
||||
|:---:|:---|:---:|:---:|
|
||||
| Ferramenta de Teste de Interação por Áudio | main》xiaozhi-server》test》test_page.html | Abrir diretamente com Google Chrome | Testa as funções de reprodução e recepção de áudio, verifica se o processamento de áudio no lado Python está normal |
|
||||
| Ferramenta de Teste de Resposta de Modelo | main》xiaozhi-server》performance_tester.py | Execute `python performance_tester.py` | Testa a velocidade de resposta dos três módulos principais: ASR(reconhecimento de fala), LLM(modelo de linguagem), VLLM(modelo de visão), TTS(síntese de voz) |
|
||||
|
||||
> 💡 Nota: Ao testar a velocidade dos modelos, apenas os modelos com chaves configuradas serão testados.
|
||||
|
||||
---
|
||||
## Lista de Funcionalidades ✨
|
||||
### Implementado ✅
|
||||

|
||||
| Módulo de Funcionalidade | Descrição |
|
||||
|:---:|:---|
|
||||
| Arquitetura Principal | Baseado em [gateway MQTT+UDP](https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/mqtt-gateway-integration.md), servidores WebSocket e HTTP, fornece sistema completo de gerenciamento de console e autenticação |
|
||||
| Interação por Voz | Suporta ASR em streaming (reconhecimento de fala), TTS em streaming (síntese de voz), VAD (detecção de atividade vocal), suporta reconhecimento multilíngue e processamento de voz |
|
||||
| Reconhecimento de Impressão Vocal | Suporta registro, gerenciamento e reconhecimento de impressão vocal de múltiplos usuários, processa em paralelo com o ASR, reconhecimento de identidade do falante em tempo real e repassa ao LLM para respostas personalizadas |
|
||||
| Diálogo Inteligente | Suporta múltiplos LLM (modelos de linguagem de grande porte), implementa diálogo inteligente |
|
||||
| Percepção Visual | Suporta múltiplos VLLM (modelos de visão de grande porte), implementa interação multimodal |
|
||||
| Reconhecimento de Intenção | Suporta reconhecimento de intenção por LLM, Function Call (chamada de função), fornece mecanismo de processamento de intenção baseado em plugins |
|
||||
| Sistema de Memória | Suporta memória local de curto prazo, memória via interface mem0ai, memória inteligente PowerMem, com funcionalidade de resumo de memória |
|
||||
| Base de Conhecimento | Suporta base de conhecimento RAGFlow, permitindo que o LLM julgue se deve acionar a base de conhecimento após receber a pergunta do usuário, e então responda à pergunta |
|
||||
| Chamada de Ferramentas | Suporta protocolo IOT do cliente, protocolo MCP do cliente, protocolo MCP do servidor, protocolo de endpoint MCP, funções de ferramentas personalizadas |
|
||||
| Envio de Comandos | Suporta envio de comandos MCP para dispositivos ESP32 via protocolo MQTT a partir do Console Inteligente |
|
||||
| Backend de Gerenciamento | Fornece interface de gerenciamento Web, suporta gerenciamento de usuários, configuração do sistema e gerenciamento de dispositivos; Suporta exibição em Chinês Simplificado, Chinês Tradicional e Inglês |
|
||||
| Ferramentas de Teste | Fornece ferramentas de teste de desempenho, ferramentas de teste de modelo de visão e ferramentas de teste de interação por áudio |
|
||||
| Suporte à Implantação | Suporta implantação via Docker e implantação local, fornece gerenciamento completo de arquivos de configuração |
|
||||
| Sistema de Plugins | Suporta extensões de plugins funcionais, desenvolvimento de plugins personalizados e carregamento dinâmico de plugins |
|
||||
|
||||
### Em Desenvolvimento 🚧
|
||||
|
||||
Para conhecer o progresso específico do plano de desenvolvimento, [clique aqui](https://github.com/users/xinnan-tech/projects/3). Perguntas frequentes e tutoriais relacionados podem ser consultados [neste link](./docs/FAQ.md)
|
||||
|
||||
Se você é um desenvolvedor de software, aqui está uma [Carta Aberta aos Desenvolvedores](docs/contributor_open_letter.md). Seja bem-vindo a participar!
|
||||
|
||||
---
|
||||
|
||||
## Ecossistema do Produto 👬
|
||||
Xiaozhi é um ecossistema. Ao utilizar este produto, você também pode conferir outros [projetos excelentes](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) neste ecossistema
|
||||
|
||||
---
|
||||
|
||||
## Lista de Plataformas/Componentes Suportados 📋
|
||||
### LLM Modelos de Linguagem
|
||||
|
||||
| Método de Uso | Plataformas Suportadas | Plataformas Gratuitas |
|
||||
|:---:|:---:|:---:|
|
||||
| Chamadas via interface OpenAI | Alibaba Bailian, Volcano Engine, DeepSeek, Zhipu, Gemini, iFLYTEK | Zhipu, Gemini |
|
||||
| Chamadas via interface Ollama | Ollama | - |
|
||||
| Chamadas via interface Dify | Dify | - |
|
||||
| Chamadas via interface FastGPT | FastGPT | - |
|
||||
| Chamadas via interface Coze | Coze | - |
|
||||
| Chamadas via interface Xinference | Xinference | - |
|
||||
| Chamadas via interface HomeAssistant | HomeAssistant | - |
|
||||
|
||||
Na verdade, qualquer LLM que suporte chamadas via interface openai pode ser integrado e utilizado.
|
||||
|
||||
---
|
||||
|
||||
### VLLM Modelos de Visão
|
||||
|
||||
| Método de Uso | Plataformas Suportadas | Plataformas Gratuitas |
|
||||
|:---:|:---:|:---:|
|
||||
| Chamadas via interface OpenAI | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
|
||||
|
||||
Na verdade, qualquer VLLM que suporte chamadas via interface OpenAI pode ser integrado e utilizado.
|
||||
|
||||
---
|
||||
|
||||
### TTS Síntese de Voz
|
||||
|
||||
| Método de Uso | Plataformas Suportadas | Plataformas Gratuitas |
|
||||
|:---:|:---:|:---:|
|
||||
| Chamadas via interface | EdgeTTS, iFLYTEK, Volcano Engine, Tencent Cloud, Alibaba Cloud e Bailian, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS, MinimaxTTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(parcial) |
|
||||
| Serviços locais | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, Index-TTS, PaddleSpeech | Index-TTS, PaddleSpeech, FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3 |
|
||||
|
||||
---
|
||||
|
||||
### VAD Detecção de Atividade Vocal
|
||||
|
||||
| Tipo | Nome da Plataforma | Método de Uso | Modelo de Preço | Observações |
|
||||
|:---:|:---------:|:----:|:----:|:--:|
|
||||
| VAD | SileroVAD | Uso local | Gratuito | |
|
||||
|
||||
---
|
||||
|
||||
### ASR Reconhecimento de Fala
|
||||
|
||||
| Método de Uso | Plataformas Suportadas | Plataformas Gratuitas |
|
||||
|:---:|:---:|:---:|
|
||||
| Uso local | FunASR, SherpaASR | FunASR, SherpaASR |
|
||||
| Chamadas via interface | FunASRServer, Volcano Engine, iFLYTEK, Tencent Cloud, Alibaba Cloud, Baidu Cloud, OpenAI ASR | FunASRServer |
|
||||
|
||||
---
|
||||
|
||||
### Reconhecimento de Impressão Vocal
|
||||
|
||||
| Método de Uso | Plataformas Suportadas | Plataformas Gratuitas |
|
||||
|:---:|:---:|:---:|
|
||||
| Uso local | 3D-Speaker | 3D-Speaker |
|
||||
|
||||
---
|
||||
|
||||
### Armazenamento de Memória
|
||||
|
||||
| Tipo | Nome da Plataforma | Método de Uso | Modelo de Preço | Observações |
|
||||
|:------:|:---------------:|:----:|:---------:|:--:|
|
||||
| Memória | mem0ai | Chamadas via interface | Cota de 1000 vezes/mês | |
|
||||
| Memória | [powermem](./docs/powermem-integration.md) | Resumo local | Depende do LLM e BD | OceanBase de código aberto, suporta busca inteligente |
|
||||
| Memória | mem_local_short | Resumo local | Gratuito | |
|
||||
| Memória | nomem | Modo sem memória | Gratuito | |
|
||||
|
||||
---
|
||||
|
||||
### Reconhecimento de Intenção
|
||||
|
||||
| Tipo | Nome da Plataforma | Método de Uso | Modelo de Preço | Observações |
|
||||
|:------:|:-------------:|:----:|:-------:|:---------------------:|
|
||||
| Intenção | intent_llm | Chamadas via interface | Baseado no preço do LLM | Reconhece intenção através de modelos de linguagem, forte generalização |
|
||||
| Intenção | function_call | Chamadas via interface | Baseado no preço do LLM | Completa a intenção através de chamada de função do modelo de linguagem, velocidade rápida, bom resultado |
|
||||
| Intenção | nointent | Modo sem intenção | Gratuito | Não realiza reconhecimento de intenção, retorna diretamente o resultado do diálogo |
|
||||
|
||||
---
|
||||
|
||||
### RAG Geração Aumentada por Recuperação
|
||||
|
||||
| Tipo | Nome da Plataforma | Método de Uso | Modelo de Preço | Observações |
|
||||
|:------:|:-------------:|:----:|:-------:|:---------------------:|
|
||||
| RAG | ragflow | Chamadas via interface | Cobrado com base nos tokens consumidos para fatiamento e segmentação de palavras | Utiliza o recurso de geração aumentada por recuperação do RagFlow para fornecer respostas de diálogo mais precisas |
|
||||
|
||||
---
|
||||
|
||||
## Agradecimentos 🙏
|
||||
|
||||
| Logo | Projeto/Empresa | Descrição |
|
||||
|:---:|:---:|:---|
|
||||
| <img src="./docs/images/logo_bailing.png" width="160"> | [Robô de Diálogo por Voz Bailing](https://github.com/wwbin2017/bailing) | Este projeto foi inspirado pelo [Robô de Diálogo por Voz Bailing](https://github.com/wwbin2017/bailing) e implementado com base nele |
|
||||
| <img src="./docs/images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Agradecimentos à [Tenclass](https://www.tenclass.com/) por formular protocolos de comunicação padrão, soluções de compatibilidade multidispositivo e demonstrações práticas de cenários de alta concorrência para o ecossistema Xiaozhi; fornecendo suporte completo de documentação técnica para este projeto |
|
||||
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology (玄凤科技)](https://github.com/Eric0308) | Agradecimentos à [Xuanfeng Technology](https://github.com/Eric0308) por contribuir com o framework de chamada de função, protocolo de comunicação MCP e implementação do mecanismo de chamada baseado em plugins. Através de um sistema padronizado de agendamento de instruções e capacidades de expansão dinâmica, melhora significativamente a eficiência de interação e extensibilidade funcional dos dispositivos de frontend (IoT) |
|
||||
| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Agradecimentos a [huangjunsen](https://github.com/huangjunsen0406) por contribuir com o módulo `Console de Controle Inteligente Mobile`, que permite controle eficiente e interação em tempo real em dispositivos móveis, melhorando significativamente a conveniência operacional e a eficiência de gerenciamento do sistema em cenários móveis. |
|
||||
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [Huiyuan Design (汇远设计)](http://ui.kwd988.net/) | Agradecimentos à [Huiyuan Design](http://ui.kwd988.net/) por fornecer soluções visuais profissionais para este projeto, utilizando sua experiência prática de design atendendo mais de mil empresas para potencializar a experiência do usuário deste produto |
|
||||
| <img src="./docs/images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology (西安勤人信息科技)](https://www.029app.com/) | Agradecimentos à [Xi'an Qinren Information Technology](https://www.029app.com/) por aprofundar o sistema visual deste projeto, garantindo consistência e extensibilidade do estilo de design geral em aplicações de múltiplos cenários |
|
||||
| <img src="./docs/images/logo_contributors.png" width="160"> | [Contribuidores de Código](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Agradecimentos a [todos os contribuidores de código](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), seus esforços tornaram o projeto mais robusto e poderoso. |
|
||||
|
||||
|
||||
<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=xinnan-tech/xiaozhi-esp32-server&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
+2
-1
@@ -21,6 +21,7 @@ Hỗ trợ giao thức MQTT+UDP, giao thức Websocket, điểm truy cập MCP,
|
||||
<a href="./README_en.md"><img alt="README in English" src="https://img.shields.io/badge/English-DFE0E5"></a>
|
||||
<a href="./README_vi.md"><img alt="Tiếng Việt" src="https://img.shields.io/badge/Tiếng Việt-DBEDFA"></a>
|
||||
<a href="./README_de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-DFE0E5"></a>
|
||||
<a href="./README_pt_BR.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-DFE0E5"></a>
|
||||
<a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">
|
||||
<img alt="GitHub Contributors" src="https://img.shields.io/github/v/release/xinnan-tech/xiaozhi-esp32-server?logo=docker" />
|
||||
</a>
|
||||
@@ -259,7 +260,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 👬
|
||||
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
|
||||
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/blob/main/README_zh.md#%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
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ pm2 restart xz-mqtt
|
||||
```
|
||||
192.168.0.7:8884
|
||||
```
|
||||
4. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_manager_api`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`UDP_PORT`。类似这样
|
||||
4. 在智控台顶部,点击`参数管理`,搜索`server.mqtt_manager_api`,点击编辑,填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`API_PORT`。类似这样
|
||||
```
|
||||
192.168.0.7:8007
|
||||
```
|
||||
@@ -154,7 +154,7 @@ curl 'http://localhost:8002/xiaozhi/ota/' \
|
||||
pm2 logs xz-mqtt
|
||||
```
|
||||
|
||||
## 第三部分:全模块运行实现小智硬件MQTT+UDP连接
|
||||
## 第三部分:单模块运行xiaozhi-server实现小智硬件MQTT+UDP连接
|
||||
|
||||
打开你的`data/.config.yaml`文件,在`server`下找到`mqtt_gateway`填入你在`.env`文件中设置的`PUBLIC_IP`+`:`+`MQTT_PORT`。类似这样
|
||||
```
|
||||
@@ -184,4 +184,4 @@ curl 'http://localhost:8002/xiaozhi/ota/' \
|
||||
唤醒后留意mqtt-gateway的日志,确认是否有连接成功的日志。
|
||||
```
|
||||
pm2 logs xz-mqtt
|
||||
```
|
||||
```
|
||||
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
'chatHistory.loading': 'Loading...',
|
||||
'chatHistory.noMoreData': 'No more data',
|
||||
'chatHistory.noChatRecords': 'No chat records',
|
||||
'chatRecordsDescription': 'Conversation records with agents will be displayed here',
|
||||
'chatHistory.chatRecordsDescription': 'Conversation records with agents will be displayed here',
|
||||
// Chat History Detail Page
|
||||
'chatHistory.pageTitle': 'Chat Detail',
|
||||
'chatHistory.assistantName': 'Intelligent Assistant',
|
||||
|
||||
@@ -8,6 +8,7 @@ import en from './en'
|
||||
import zh_TW from './zh_TW'
|
||||
import de from './de'
|
||||
import vi from './vi'
|
||||
import pt_BR from './pt_BR'
|
||||
|
||||
// 语言包映射
|
||||
const messages = {
|
||||
@@ -16,6 +17,7 @@ const messages = {
|
||||
zh_TW: zh_TW,
|
||||
de,
|
||||
vi,
|
||||
pt_BR: pt_BR,
|
||||
}
|
||||
|
||||
// 当前使用的语言
|
||||
@@ -72,5 +74,6 @@ export function getSupportedLanguages(): { code: Language, name: string }[] {
|
||||
{ code: 'zh_TW', name: '繁體中文' },
|
||||
{ code: 'de', name: 'Deutsch' },
|
||||
{ code: 'vi', name: 'Tiếng Việt' },
|
||||
{ code: 'pt_BR', name: 'Português (Brasil)' },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
// Pacote de idioma Português Brasileiro
|
||||
export default {
|
||||
// TabBar
|
||||
'tabBar.home': 'Início',
|
||||
'tabBar.deviceConfig': 'Config. de Rede',
|
||||
'tabBar.settings': 'Sistema',
|
||||
// Settings page title
|
||||
'settings.title': 'Configurações',
|
||||
// Login page
|
||||
'login.pageTitle': 'Entrar',
|
||||
'login.navigationTitle': 'Entrar',
|
||||
'login.fetchConfigError': 'Falha ao buscar configuração:',
|
||||
'login.selectLanguage': 'Selecionar Idioma',
|
||||
'login.selectLanguageTip': '中文',
|
||||
'login.welcomeBack': 'Bem-vindo de Volta',
|
||||
'login.pleaseLogin': 'Por favor, entre na sua conta',
|
||||
'login.enterUsername': 'Por favor, insira o nome de usuário',
|
||||
'login.enterPassword': 'Por favor, insira a senha',
|
||||
'login.enterCaptcha': 'Por favor, insira o código de verificação',
|
||||
'login.loginButton': 'Entrar',
|
||||
'login.loggingIn': 'Entrando...',
|
||||
'login.noAccount': 'Cadastrar',
|
||||
'login.enterPhone': 'Por favor, insira o número de telefone',
|
||||
'login.selectCountry': 'Selecionar País/Região',
|
||||
'login.confirm': 'Confirmar',
|
||||
'login.serverSetting': 'Configurações do Servidor',
|
||||
'login.requiredUsername': 'O nome de usuário não pode estar vazio',
|
||||
'login.requiredPassword': 'A senha não pode estar vazia',
|
||||
'login.requiredCaptcha': 'O código de verificação não pode estar vazio',
|
||||
'login.requiredMobile': 'Por favor, insira um número de telefone válido',
|
||||
'login.captchaError': 'Erro no código de verificação gráfico',
|
||||
'login.forgotPassword': 'Esqueceu a Senha',
|
||||
|
||||
// Register page
|
||||
'register.pageTitle': 'Cadastro',
|
||||
'register.createAccount': 'Criar Conta',
|
||||
'register.enterUsername': 'Por favor, insira o nome de usuário',
|
||||
'register.enterPassword': 'Por favor, insira a senha',
|
||||
'register.confirmPassword': 'Por favor, confirme a senha',
|
||||
'register.enterPhone': 'Por favor, insira o número de telefone',
|
||||
'register.enterCode': 'Por favor, insira o código de verificação',
|
||||
'register.getCode': 'Obter Código',
|
||||
'register.agreeTerms': 'Li e concordo com os',
|
||||
'register.terms': 'Termos de Uso',
|
||||
'register.privacy': 'Política de Privacidade',
|
||||
'register.registerButton': 'Cadastrar',
|
||||
'register.registering': 'Cadastrando...',
|
||||
'register.haveAccount': 'Já tem uma conta?',
|
||||
'register.loginNow': 'Entrar Agora',
|
||||
'register.selectCountry': 'Selecionar País/Região',
|
||||
'register.confirm': 'Confirmar',
|
||||
'register.captchaSendSuccess': 'Código de verificação enviado com sucesso',
|
||||
|
||||
// Home page
|
||||
'home.pageTitle': 'Início',
|
||||
'home.createAgent': 'Criar Agente',
|
||||
'home.agentName': 'Agente',
|
||||
'home.modelInfo': 'Info do Modelo',
|
||||
'home.lastActive': 'Última Atividade',
|
||||
'home.greeting': 'Olá Jarvis',
|
||||
'home.subtitle': 'Vamos ter',
|
||||
'home.wonderfulDay': 'um dia maravilhoso!',
|
||||
'home.emptyState': 'Nenhum agente disponível',
|
||||
'home.deviceManagement': 'Gerenciamento de Dispositivos',
|
||||
'home.lastConversation': 'Última Conversa:',
|
||||
'home.delete': 'Excluir',
|
||||
'home.createFirstAgent': 'Clique no botão + no canto inferior direito para criar seu primeiro agente',
|
||||
'home.dialogTitle': 'Criar Agente',
|
||||
'home.inputPlaceholder': 'ex: Assistente de Atendimento, Assistente de Voz, Perguntas e Respostas',
|
||||
'home.createError': 'Por favor, insira o nome do agente',
|
||||
'home.createNow': 'Criar Agora',
|
||||
'home.justNow': 'Agora mesmo',
|
||||
'home.minutesAgo': 'minutos atrás',
|
||||
'home.hoursAgo': 'horas atrás',
|
||||
'home.daysAgo': 'dias atrás',
|
||||
|
||||
// Agent page
|
||||
'agent.pageTitle': 'Agente',
|
||||
'agent.roleConfig': 'Configuração de Papel',
|
||||
'agent.deviceManagement': 'Gerenciamento de Dispositivos',
|
||||
'agent.chatHistory': 'Histórico de Conversas',
|
||||
'agent.voiceprintManagement': 'Gerenciamento de Impressão Vocal',
|
||||
'agent.editTitle': 'Editar Agente',
|
||||
'agent.toolsTitle': 'Editar Funcionalidades',
|
||||
'agent.voiceActivityDetection': 'Detecção de Atividade de Voz',
|
||||
'agent.speechRecognition': 'Reconhecimento de Fala',
|
||||
'agent.largeLanguageModel': 'Modelo de Linguagem Grande',
|
||||
'agent.save': 'Salvar',
|
||||
'agent.cancel': 'Cancelar',
|
||||
// Agent Edit Page
|
||||
'agent.basicInfo': 'Informações Básicas',
|
||||
'agent.agentName': 'Nome do Agente',
|
||||
'agent.inputAgentName': 'Por favor, insira o nome do agente',
|
||||
'agent.roleMode': 'Modo de Papel',
|
||||
'agent.roleDescription': 'Descrição do Papel',
|
||||
'agent.inputRoleDescription': 'Por favor, insira a descrição do papel',
|
||||
'agent.modelConfig': 'Configuração do Modelo',
|
||||
'agent.vad': 'Detecção de Atividade de Voz',
|
||||
'agent.asr': 'Reconhecimento de Fala',
|
||||
'agent.llm': 'Modelo de Linguagem Grande',
|
||||
'agent.vllm': 'Modelo de Linguagem Visual',
|
||||
'agent.intent': 'Reconhecimento de Intenção',
|
||||
'agent.memory': 'Memória',
|
||||
'agent.voiceSettings': 'Configurações de Voz',
|
||||
'agent.tts': 'Texto para Fala',
|
||||
'agent.voiceprint': 'Voz do Agente',
|
||||
'agent.plugins': 'Plugins',
|
||||
'agent.editFunctions': 'Editar Funções',
|
||||
'agent.historyMemory': 'Memória de Histórico',
|
||||
'agent.memoryContent': 'Conteúdo da memória',
|
||||
'agent.saving': 'Salvando...',
|
||||
'agent.saveSuccess': 'Salvo com sucesso',
|
||||
'agent.saveFail': 'Falha ao salvar',
|
||||
'agent.loadFail': 'Falha ao carregar',
|
||||
'agent.pleaseInputAgentName': 'Por favor, insira o nome do agente',
|
||||
'agent.pleaseInputRoleDescription': 'Por favor, insira a descrição do papel',
|
||||
'agent.pleaseSelect': 'Por favor, selecione',
|
||||
|
||||
// Chat History Page
|
||||
'chatHistory.getChatSessions': 'Obter lista de sessões de conversa',
|
||||
'chatHistory.noSelectedAgent': 'Nenhum agente selecionado',
|
||||
'chatHistory.getChatSessionsFailed': 'Falha ao obter lista de sessões de conversa:',
|
||||
'chatHistory.unknownTime': 'Hora desconhecida',
|
||||
'chatHistory.justNow': 'Agora mesmo',
|
||||
'chatHistory.minutesAgo': '{minutes} minutos atrás',
|
||||
'chatHistory.hoursAgo': '{hours} horas atrás',
|
||||
'chatHistory.daysAgo': '{days} dias atrás',
|
||||
'chatHistory.conversationRecord': 'Registro de Conversas',
|
||||
'chatHistory.totalChats': '{count} conversas no total',
|
||||
'chatHistory.loading': 'Carregando...',
|
||||
'chatHistory.noMoreData': 'Não há mais dados',
|
||||
'chatHistory.noChatRecords': 'Nenhum registro de conversa',
|
||||
'chatHistory.chatRecordsDescription': 'Os registros de conversas com agentes serão exibidos aqui',
|
||||
// Chat History Detail Page
|
||||
'chatHistory.pageTitle': 'Detalhe da Conversa',
|
||||
'chatHistory.assistantName': 'Assistente Inteligente',
|
||||
'chatHistory.userName': 'Usuário',
|
||||
'chatHistory.aiAssistantName': 'Assistente IA',
|
||||
'chatHistory.loadFailed': 'Falha ao carregar histórico de conversas',
|
||||
'chatHistory.parameterError': 'Erro de parâmetro da página',
|
||||
'chatHistory.invalidAudioId': 'ID de áudio inválido',
|
||||
'chatHistory.audioPlayFailed': 'Falha na reprodução de áudio',
|
||||
'chatHistory.playAudioFailed': 'Falha ao reproduzir áudio',
|
||||
|
||||
// Device management page
|
||||
'device.pageTitle': 'Gerenciamento de Dispositivos',
|
||||
'device.noDevices': 'Nenhum dispositivo disponível',
|
||||
'device.macAddress': 'Endereço MAC',
|
||||
'device.firmwareVersion': 'Versão do Firmware',
|
||||
'device.lastConnected': 'Última Conversa',
|
||||
'device.otaUpdate': 'Atualização OTA',
|
||||
'device.unbind': 'Desvincular',
|
||||
'device.confirmUnbind': 'Confirmar',
|
||||
'device.bindDevice': 'Vincular Novo Dispositivo',
|
||||
'device.deviceType': 'Tipo de Dispositivo',
|
||||
'device.loading': 'Carregando...',
|
||||
'device.neverConnected': 'Nunca conectado',
|
||||
'device.justNow': 'Agora mesmo',
|
||||
'device.minutesAgo': '{minutes} minutos atrás',
|
||||
'device.hoursAgo': '{hours} horas atrás',
|
||||
'device.daysAgo': '{days} dias atrás',
|
||||
'device.otaAutoUpdateEnabled': 'Atualização automática OTA ativada',
|
||||
'device.otaAutoUpdateDisabled': 'Atualização automática OTA desativada',
|
||||
'device.operationFailed': 'Operação falhou, por favor tente novamente',
|
||||
'device.deviceUnbound': 'Dispositivo desvinculado',
|
||||
'device.unbindFailed': 'Falha ao desvincular, por favor tente novamente',
|
||||
'device.unbindDevice': 'Desvincular Dispositivo',
|
||||
'device.confirmUnbindDevice': 'Tem certeza que deseja desvincular o dispositivo "{macAddress}"?',
|
||||
'device.cancel': 'Cancelar',
|
||||
'device.noDevice': 'Nenhum Dispositivo',
|
||||
'device.pleaseSelectAgent': 'Por favor, selecione um agente primeiro',
|
||||
'device.deviceBindSuccess': 'Dispositivo vinculado com sucesso!',
|
||||
'device.bindFailed': 'Falha ao vincular, por favor verifique se o código de verificação está correto',
|
||||
'device.enterDeviceCode': 'Por favor, insira o código de verificação do dispositivo',
|
||||
'device.bindNow': 'Vincular Agora',
|
||||
'device.lastConnection': 'Última Conexão',
|
||||
'device.clickToBindFirstDevice': 'Clique no botão + no canto inferior direito para vincular seu primeiro dispositivo',
|
||||
|
||||
// Common
|
||||
'common.success': 'Sucesso',
|
||||
'common.fail': 'Falhou',
|
||||
'common.loading': 'Carregando...',
|
||||
'common.confirm': 'Confirmar',
|
||||
'common.cancel': 'Cancelar',
|
||||
'common.delete': 'Excluir',
|
||||
'common.edit': 'Editar',
|
||||
'common.add': 'Adicionar',
|
||||
'common.pleaseSelect': 'Por favor, selecione',
|
||||
'common.unknownError': 'Erro desconhecido',
|
||||
'common.networkError': 'Erro de rede',
|
||||
|
||||
// Retrieve Password page
|
||||
'retrievePassword.title': 'Redefinir Senha',
|
||||
'retrievePassword.subtitle': 'Recupere a senha da sua conta pelo número de celular',
|
||||
'retrievePassword.mobileRequired': 'Por favor, insira o número de celular',
|
||||
'retrievePassword.inputCorrectMobile': 'Por favor, insira um número de celular válido',
|
||||
'retrievePassword.captchaRequired': 'Por favor, insira o código de verificação gráfico',
|
||||
'retrievePassword.mobileCaptchaRequired': 'Por favor, insira o código de verificação por SMS',
|
||||
'retrievePassword.newPasswordRequired': 'Por favor, insira a nova senha',
|
||||
'retrievePassword.confirmNewPasswordRequired': 'Por favor, confirme a nova senha',
|
||||
'retrievePassword.passwordsNotMatch': 'As senhas não coincidem',
|
||||
'retrievePassword.mobilePlaceholder': 'Por favor, insira o número de celular',
|
||||
'retrievePassword.captchaPlaceholder': 'Por favor, insira o código de verificação gráfico',
|
||||
'retrievePassword.mobileCaptchaPlaceholder': 'Por favor, insira o código de verificação por SMS',
|
||||
'retrievePassword.newPasswordPlaceholder': 'Por favor, insira a nova senha',
|
||||
'retrievePassword.confirmNewPasswordPlaceholder': 'Por favor, confirme a nova senha',
|
||||
'retrievePassword.getMobileCaptcha': 'Obter Código',
|
||||
'retrievePassword.captchaSendSuccess': 'Código de verificação enviado com sucesso',
|
||||
'retrievePassword.passwordUpdateSuccess': 'Senha redefinida com sucesso',
|
||||
'retrievePassword.resetButton': 'Redefinir Senha',
|
||||
'retrievePassword.goToLogin': 'Voltar ao Login',
|
||||
|
||||
// SM2 encryption related error messages
|
||||
'sm2.publicKeyNotConfigured': 'Chave pública SM2 não configurada, por favor entre em contato com o administrador',
|
||||
'sm2.encryptionFailed': 'Falha na criptografia da senha',
|
||||
'sm2.keyGenerationFailed': 'Falha na geração do par de chaves',
|
||||
'sm2.invalidPublicKey': 'Formato de chave pública inválido',
|
||||
'sm2.encryptionError': 'Ocorreu um erro durante a criptografia',
|
||||
'sm2.publicKeyRetry': 'Tentando obter a chave pública novamente...',
|
||||
'sm2.publicKeyRetryFailed': 'Falha na nova tentativa de obter a chave pública',
|
||||
|
||||
// Voiceprint page
|
||||
'voiceprint.noSelectedAgent': 'Nenhum agente selecionado',
|
||||
'voiceprint.pleaseSelectAgent': 'Por favor, selecione um agente primeiro',
|
||||
'voiceprint.fetchHistoryFailed': 'Falha ao buscar histórico de conversas',
|
||||
'voiceprint.clickToSelectVector': 'Clique para selecionar vetor de impressão vocal',
|
||||
'voiceprint.pleaseInputName': 'Por favor, insira o nome',
|
||||
'voiceprint.pleaseSelectVector': 'Por favor, selecione o vetor de impressão vocal',
|
||||
'voiceprint.addSuccess': 'Adicionado com sucesso',
|
||||
'voiceprint.addFailed': 'Falha ao adicionar locutor',
|
||||
'voiceprint.editSuccess': 'Editado com sucesso',
|
||||
'voiceprint.editFailed': 'Falha ao editar locutor',
|
||||
'voiceprint.deleteConfirmMsg': 'Tem certeza que deseja excluir este locutor?',
|
||||
'voiceprint.deleteConfirmTitle': 'Confirmar Exclusão',
|
||||
'voiceprint.deleteSuccess': 'Excluído com sucesso',
|
||||
'voiceprint.loading': 'Carregando...',
|
||||
'voiceprint.delete': 'Excluir',
|
||||
'voiceprint.emptyTitle': 'Nenhum dado de impressão vocal',
|
||||
'voiceprint.emptyDesc': 'Clique no botão + no canto inferior direito para adicionar seu primeiro locutor',
|
||||
'voiceprint.addSpeaker': 'Adicionar Locutor',
|
||||
'voiceprint.voiceVector': 'Vetor de Impressão Vocal',
|
||||
'voiceprint.name': 'Nome',
|
||||
'voiceprint.description': 'Descrição',
|
||||
'voiceprint.pleaseInputDescription': 'Por favor, insira a descrição',
|
||||
'voiceprint.cancel': 'Cancelar',
|
||||
'voiceprint.save': 'Salvar',
|
||||
'voiceprint.editSpeaker': 'Editar Locutor',
|
||||
'voiceprint.selectVector': 'Selecionar Vetor de Impressão Vocal',
|
||||
'voiceprint.voiceprintInterfaceNotConfigured': 'Interface de impressão vocal não configurada',
|
||||
|
||||
// Settings page
|
||||
'settings.pageTitle': 'Configurações',
|
||||
'settings.navigationTitle': 'Configurações',
|
||||
'settings.networkSettings': 'Configurações de Rede',
|
||||
'settings.serverApiUrl': 'URL da API do Servidor',
|
||||
'settings.validServerUrl': 'Por favor, insira um endereço de servidor válido (começando com http ou https e terminando com /xiaozhi)',
|
||||
'settings.saveSettings': 'Salvar Configurações',
|
||||
'settings.resetDefault': 'Restaurar Padrão',
|
||||
'settings.restartApp': 'Reiniciar App',
|
||||
'settings.restartNow': 'Reiniciar Agora',
|
||||
'settings.restartLater': 'Depois',
|
||||
// About us
|
||||
'settings.aboutApp': 'Sobre o Console XiaoZhi',
|
||||
'settings.aboutContent': 'Console XiaoZhi\n\nUm aplicativo móvel de gerenciamento multiplataforma construído com Vue.js 3 + uni-app, oferecendo gerenciamento de dispositivos, configuração de agentes e outras funcionalidades para o hardware inteligente xiaozhi ESP32.\n\n© 2025 xiaozhi-esp32-server {version}',
|
||||
'settings.restartSuccess': 'Salvo, você pode reiniciar o app manualmente depois',
|
||||
'settings.serverUrlSavedAndCacheCleared': 'URL do servidor salva e cache limpo',
|
||||
'settings.resetToDefaultAndCacheCleared': 'Restaurado ao padrão e cache limpo',
|
||||
'settings.resetSuccess': 'Restauração bem-sucedida',
|
||||
'settings.enterServerUrl': 'Por favor, insira a URL do servidor',
|
||||
'settings.clearCacheFailed': 'Falha ao limpar o cache',
|
||||
'settings.cacheManagement': 'Gerenciamento de Cache',
|
||||
'settings.totalCacheSize': 'Tamanho Total do Cache',
|
||||
'settings.appDataSize': 'Tamanho Total dos Dados do App',
|
||||
'settings.cacheClear': 'Limpar Cache',
|
||||
'settings.clearAllCache': 'Limpar todos os dados de cache',
|
||||
'settings.clearCache': 'Limpar Cache',
|
||||
'settings.modifyWillClearCache': 'Modificações irão limpar o cache',
|
||||
'settings.appInfo': 'Informações do App',
|
||||
'settings.aboutUs': 'Sobre Nós',
|
||||
'settings.appVersion': 'Versão do App e Info da Equipe',
|
||||
'settings.confirmClear': 'Confirmar Limpeza',
|
||||
'settings.confirmClearMessage': 'Tem certeza que deseja limpar todo o cache? Isso excluirá todos os dados, incluindo o status de login, e exigirá novo login.',
|
||||
'settings.cacheCleared': 'Cache limpo com sucesso, redirecionando para a página de login',
|
||||
'settings.languageSettings': 'Configurações de Idioma',
|
||||
'settings.language': 'Idioma',
|
||||
'settings.selectLanguage': 'Selecionar Idioma',
|
||||
'settings.languageChanged': 'Idioma alterado com sucesso',
|
||||
|
||||
// Messages
|
||||
'message.loginSuccess': 'Login realizado com sucesso!',
|
||||
'message.loginFail': 'Falha no login',
|
||||
'message.registerSuccess': 'Cadastro realizado com sucesso',
|
||||
'message.registerFail': 'Falha no cadastro',
|
||||
'message.saveSuccess': 'Salvo com sucesso',
|
||||
'message.saveFail': 'Falha ao salvar',
|
||||
'message.deleteSuccess': 'Excluído com sucesso',
|
||||
'message.deleteFail': 'Falha ao excluir',
|
||||
'message.bindSuccess': 'Vinculação realizada com sucesso',
|
||||
'message.bindFail': 'Falha na vinculação',
|
||||
'message.unbindSuccess': 'Desvinculação realizada com sucesso',
|
||||
'message.unbindFail': 'Falha na desvinculação',
|
||||
'message.networkError': 'Erro de rede, por favor verifique sua conexão',
|
||||
'message.serverError': 'Erro no servidor, por favor tente novamente mais tarde',
|
||||
'message.invalidAddress': 'Endereço inválido, por favor verifique se o servidor está iniciado ou se a conexão de rede está normal',
|
||||
'message.languageChanged': 'Idioma alterado',
|
||||
'message.passwordError': 'Conta ou senha incorretos',
|
||||
'message.phoneRegistered': 'Este número de telefone já está cadastrado',
|
||||
|
||||
// Agent Tools Page
|
||||
'agent.tools.pageTitle': 'Ferramentas do Agente',
|
||||
'agent.tools.unselected': 'Não selecionado',
|
||||
'agent.tools.selected': 'Selecionado',
|
||||
'agent.tools.noMorePlugins': 'Não há mais plugins',
|
||||
'agent.tools.pleaseSelectPlugin': 'Por favor, selecione a função do plugin',
|
||||
'agent.tools.builtInPlugins': 'Plugins Integrados',
|
||||
'agent.tools.mcpAccessPoint': 'Ponto de Acesso MCP',
|
||||
'agent.tools.copy': 'Copiar',
|
||||
'agent.tools.noTools': 'Nenhuma ferramenta disponível',
|
||||
'agent.tools.parameterConfig': 'Configuração de Parâmetros',
|
||||
'agent.tools.noParamsNeeded': 'Nenhum parâmetro necessário',
|
||||
'agent.tools.pleaseInput': 'Por favor, insira',
|
||||
'agent.tools.inputOneItemPerLine': 'Insira um item por linha',
|
||||
'agent.tools.pleaseInputValidJson': 'Por favor, insira um formato JSON válido',
|
||||
'agent.tools.enableFunction': 'Ativar Função',
|
||||
'agent.tools.toggleFunction': 'Ativar ou desativar esta função',
|
||||
'agent.tools.jsonFormatError': 'Erro no formato JSON',
|
||||
'agent.tools.noMcpAddressToCopy': 'Nenhum endereço MCP para copiar',
|
||||
'agent.tools.mcpAddressCopied': 'Endereço MCP copiado para a área de transferência',
|
||||
'agent.tools.copyFailed': 'Falha ao copiar, por favor tente novamente',
|
||||
'agent.tools.defaultValue': 'Valor padrão',
|
||||
'agent.tools.notSelected': 'Não selecionado',
|
||||
'agent.tools.clickToConfigure': 'Clique para configurar',
|
||||
'agent.tools.mcpEndpoint': 'Endpoint MCP',
|
||||
'agent.tools.eachLineOneItem': 'Insira um item por linha',
|
||||
|
||||
// Device Config page
|
||||
'deviceConfig.pageTitle': 'Configuração do Dispositivo',
|
||||
'deviceConfig.wifiConfig': 'Configuração WiFi',
|
||||
'deviceConfig.ultrasonicConfig': 'Configuração Ultrassônica',
|
||||
'deviceConfig.selectConfigMethod': 'Selecionar Método de Configuração',
|
||||
'deviceConfig.networkConfig': 'Configuração de Rede',
|
||||
'deviceConfig.selectedNetwork': 'Rede Selecionada',
|
||||
'deviceConfig.signal': 'Sinal',
|
||||
'deviceConfig.openNetwork': 'Rede Aberta',
|
||||
'deviceConfig.encryptedNetwork': 'Rede Criptografada',
|
||||
'deviceConfig.password': 'Senha',
|
||||
'deviceConfig.pleaseEnterPassword': 'Por favor, insira a senha do WiFi',
|
||||
'deviceConfig.startConfig': 'Iniciar Configuração',
|
||||
'deviceConfig.connectToXiaozhiHotspot': 'Por favor, conecte-se ao hotspot xiaozhi primeiro',
|
||||
'deviceConfig.detecting': 'Detectando...',
|
||||
'deviceConfig.reDetect': 'Detectar Novamente',
|
||||
'deviceConfig.alreadyConnected': 'Conectado ao hotspot xiaozhi',
|
||||
'deviceConfig.refreshStatus': 'Atualizar Status',
|
||||
'deviceConfig.wifiNetworks': 'Redes WiFi',
|
||||
'deviceConfig.selectWifiNetwork': 'Selecionar Rede WiFi',
|
||||
'deviceConfig.refreshScan': 'Atualizar Busca',
|
||||
'deviceConfig.noWifiNetworks': 'Nenhuma rede WiFi disponível',
|
||||
'deviceConfig.clickToRefreshScan': 'Por favor, clique em Atualizar Busca',
|
||||
'deviceConfig.signalStrong': 'Sinal Forte',
|
||||
'deviceConfig.signalGood': 'Sinal Bom',
|
||||
'deviceConfig.signalFair': 'Sinal Regular',
|
||||
'deviceConfig.signalWeak': 'Sinal Fraco',
|
||||
'deviceConfig.channel': 'Canal',
|
||||
'deviceConfig.about': 'aproximadamente',
|
||||
'deviceConfig.seconds': 'segundos',
|
||||
'deviceConfig.generating': 'Gerando...',
|
||||
'deviceConfig.playing': 'Reproduzindo...',
|
||||
'deviceConfig.generateAndPlaySoundWave': 'Gerar e Reproduzir Onda Sonora',
|
||||
'deviceConfig.playSoundWave': 'Reproduzir Onda Sonora',
|
||||
'deviceConfig.stopPlaying': 'Parar Reprodução',
|
||||
'deviceConfig.autoLoopPlaySoundWave': 'Reproduzir Onda Sonora em Loop Automático',
|
||||
'deviceConfig.configAudioFile': 'Arquivo de Áudio de Configuração',
|
||||
'deviceConfig.duration': 'Duração',
|
||||
'deviceConfig.ultrasonicConfigInstructions': 'Instruções de Configuração Ultrassônica',
|
||||
'deviceConfig.ensureWifiNetworkSelectedAndPasswordEntered': 'Certifique-se de que a rede WiFi foi selecionada e a senha foi inserida',
|
||||
'deviceConfig.clickGenerateAndPlaySoundWave': 'Clique em Gerar e Reproduzir Onda Sonora, o sistema codificará as informações de configuração em áudio',
|
||||
'deviceConfig.bringPhoneCloseToXiaozhiDevice': 'Aproxime o celular do dispositivo xiaozhi (distância de 1-2 metros)',
|
||||
'deviceConfig.duringAudioPlaybackXiaozhiWillReceive': 'Durante a reprodução do áudio, o xiaozhi receberá e decodificará as informações de configuração',
|
||||
'deviceConfig.afterConfigSuccessDeviceWillConnect': 'Após a configuração bem-sucedida, o dispositivo se conectará automaticamente à rede WiFi',
|
||||
'deviceConfig.usesAfskModulation': 'Utiliza tecnologia de modulação AFSK, transmitindo dados pelas frequências de 1800Hz e 1500Hz',
|
||||
'deviceConfig.ensureModeratePhoneVolume': 'Por favor, certifique-se de que o volume do celular está moderado para evitar interferência de ruídos ambientais',
|
||||
'deviceConfig.generatingUltrasonicConfigAudio': 'Gerando áudio de configuração ultrassônica',
|
||||
'deviceConfig.configData': 'Dados de configuração',
|
||||
'deviceConfig.dataBytesLength': 'Comprimento dos bytes de dados',
|
||||
'deviceConfig.bitStreamLength': 'Comprimento do fluxo de bits',
|
||||
'deviceConfig.base64Length': 'Comprimento Base64',
|
||||
'deviceConfig.audioFileTooLarge': 'Arquivo de áudio muito grande, por favor reduza o tamanho do SSID ou da senha',
|
||||
'deviceConfig.audioGenerationSuccess': 'Áudio gerado com sucesso',
|
||||
'deviceConfig.samplePoints': 'Pontos de amostra',
|
||||
'deviceConfig.soundWaveGenerationSuccess': 'Onda sonora gerada com sucesso',
|
||||
'deviceConfig.audioGenerationFailed': 'Falha na geração do áudio',
|
||||
'deviceConfig.soundWaveGenerationFailed': 'Falha na geração da onda sonora',
|
||||
'deviceConfig.pleaseGenerateAudioFirst': 'Por favor, gere o áudio primeiro',
|
||||
'deviceConfig.startPlayingUltrasonicConfigAudio': 'Iniciando reprodução do áudio de configuração ultrassônica',
|
||||
'deviceConfig.ultrasonicAudioStartedPlaying': 'Reprodução do áudio ultrassônico iniciada',
|
||||
'deviceConfig.startPlayingConfigSoundWave': 'Reprodução da onda sonora de configuração iniciada',
|
||||
'deviceConfig.ultrasonicAudioPlaybackEnded': 'Reprodução do áudio ultrassônico finalizada',
|
||||
'deviceConfig.audioPlaybackFailed': 'Falha na reprodução do áudio',
|
||||
'deviceConfig.audioResourceBusy': 'Recurso de áudio ocupado, por favor tente novamente mais tarde',
|
||||
'deviceConfig.audioFormatNotSupported': 'Formato de áudio não suportado, pode ser um problema de URI de dados',
|
||||
'deviceConfig.audioFileError': 'Erro no arquivo de áudio',
|
||||
'deviceConfig.cleaningUpAudioContext': 'Limpando contexto de áudio',
|
||||
'deviceConfig.cleaningUpAudioContextFailed': 'Falha ao limpar contexto de áudio',
|
||||
'deviceConfig.stoppedPlayingUltrasonicAudio': 'Reprodução do áudio ultrassônico interrompida',
|
||||
'deviceConfig.stoppedPlaying': 'Reprodução interrompida',
|
||||
'deviceConfig.configMethod': 'Método de Configuração',
|
||||
'deviceConfig.enterWifiPassword': 'Por favor, insira a senha do WiFi',
|
||||
'deviceConfig.xiaozhi': 'xiaozhi',
|
||||
'deviceConfig.connectXiaozhiHotspot': 'Por favor, conecte-se ao hotspot xiaozhi',
|
||||
'deviceConfig.wifiScanResponse': 'Resposta da busca WiFi',
|
||||
'deviceConfig.scanSuccess': 'Busca bem-sucedida',
|
||||
'deviceConfig.networks': 'redes',
|
||||
'deviceConfig.wifiScanFailed': 'Falha na busca WiFi',
|
||||
'deviceConfig.scanFailedCheckConnection': 'Falha na busca, por favor verifique a conexão',
|
||||
'deviceConfig.checking': 'Verificando',
|
||||
'deviceConfig.reCheck': 'Verificar Novamente',
|
||||
'deviceConfig.connectedXiaozhiHotspot': 'Conectado ao hotspot xiaozhi',
|
||||
'deviceConfig.wifiNetwork': 'Rede WiFi',
|
||||
'deviceConfig.scanning': 'Buscando',
|
||||
'deviceConfig.cancel': 'Cancelar',
|
||||
'deviceConfig.clickRefreshScan': 'Por favor, clique em Atualizar Busca',
|
||||
'deviceConfig.esp32ConnectionCheckFailed': 'Falha na verificação de conexão ESP32',
|
||||
'deviceConfig.startWifiConfig': 'Iniciando configuração WiFi',
|
||||
'deviceConfig.configSuccess': 'Configuração bem-sucedida',
|
||||
'deviceConfig.deviceWillConnectTo': 'O dispositivo se conectará a',
|
||||
'deviceConfig.deviceWillRestart': 'O dispositivo será reiniciado',
|
||||
'deviceConfig.pleaseDisconnectXiaozhiHotspot': 'Por favor, desconecte-se do hotspot xiaozhi',
|
||||
'deviceConfig.configFailed': 'Falha na configuração',
|
||||
'deviceConfig.wifiConfigFailed': 'Falha na configuração WiFi',
|
||||
'deviceConfig.pleaseCheckNetworkConnection': 'Por favor, verifique a conexão de rede',
|
||||
'deviceConfig.startWifiConfigButton': 'Iniciar Configuração',
|
||||
'deviceConfig.wifiConfigInstructions': 'Instruções de Configuração WiFi',
|
||||
'deviceConfig.phoneConnectXiaozhiHotspot': 'Conecte o celular ao hotspot xiaozhi',
|
||||
'deviceConfig.selectTargetWifiNetwork': 'Selecione a rede WiFi de destino',
|
||||
'deviceConfig.enterWifiPasswordIfNeeded': 'Insira a senha do WiFi se necessário',
|
||||
'deviceConfig.clickStartConfigAndWait': 'Clique em Iniciar Configuração e aguarde',
|
||||
'deviceConfig.afterConfigSuccessDeviceWillRestart': 'Após a configuração bem-sucedida, o dispositivo reiniciará automaticamente',
|
||||
'deviceConfig.audioPlaybackError': 'Erro na reprodução de áudio',
|
||||
'deviceConfig.playbackFailed': 'Falha na reprodução',
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
// 支持的语言类型
|
||||
export type Language = 'zh_CN' | 'en' | 'zh_TW' | 'de' | 'vi'
|
||||
export type Language = 'zh_CN' | 'en' | 'zh_TW' | 'de' | 'vi' | 'pt_BR'
|
||||
|
||||
export interface LangStore {
|
||||
currentLang: Language
|
||||
|
||||
@@ -254,6 +254,8 @@ export default {
|
||||
return this.$t("language.de");
|
||||
case "vi":
|
||||
return this.$t("language.vi");
|
||||
case "pt_BR":
|
||||
return this.$t("language.ptBR");
|
||||
default:
|
||||
return this.$t("language.zhCN");
|
||||
}
|
||||
@@ -272,6 +274,8 @@ export default {
|
||||
return require("@/assets/xiaozhi-ai_de.png");
|
||||
case "vi":
|
||||
return require("@/assets/xiaozhi-ai_vi.png");
|
||||
case "pt_BR":
|
||||
return require("@/assets/xiaozhi-ai_en.png");
|
||||
default:
|
||||
return require("@/assets/xiaozhi-ai.png");
|
||||
}
|
||||
@@ -303,6 +307,10 @@ export default {
|
||||
label: this.$t("language.vi"),
|
||||
value: "vi",
|
||||
},
|
||||
{
|
||||
label: this.$t("language.ptBR"),
|
||||
value: "pt_BR",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -663,6 +663,7 @@ export default {
|
||||
'language.en': 'English',
|
||||
'language.de': 'Deutsch',
|
||||
'language.vi': 'Tiếng Việt',
|
||||
'language.ptBR': 'Português (Brasil)',
|
||||
|
||||
// Home page text
|
||||
'home.addAgent': 'Agent hinzufügen',
|
||||
|
||||
@@ -663,6 +663,7 @@ export default {
|
||||
'language.en': 'English',
|
||||
'language.de': 'Deutsch',
|
||||
'language.vi': 'Tiếng Việt',
|
||||
'language.ptBR': 'Português (Brasil)',
|
||||
|
||||
// Home page text
|
||||
'home.addAgent': 'Add Agent',
|
||||
|
||||
@@ -5,6 +5,7 @@ import zhTW from './zh_TW';
|
||||
import en from './en';
|
||||
import de from './de';
|
||||
import vi from './vi';
|
||||
import ptBR from './pt_BR';
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
@@ -27,6 +28,9 @@ const getDefaultLanguage = () => {
|
||||
if (browserLang.indexOf('vi') === 0) {
|
||||
return 'vi';
|
||||
}
|
||||
if (browserLang === 'pt-BR' || browserLang === 'pt') {
|
||||
return 'pt_BR';
|
||||
}
|
||||
return 'en';
|
||||
};
|
||||
|
||||
@@ -38,7 +42,8 @@ const i18n = new VueI18n({
|
||||
'zh_TW': zhTW,
|
||||
'en': en,
|
||||
'de': de,
|
||||
'vi': vi
|
||||
'vi': vi,
|
||||
'pt_BR': ptBR
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -663,6 +663,7 @@ export default {
|
||||
'language.en': 'English',
|
||||
'language.de': 'Deutsch',
|
||||
'language.vi': 'Tiếng Việt',
|
||||
'language.ptBR': 'Português (Brasil)',
|
||||
|
||||
// Home page text
|
||||
'home.addAgent': 'Thêm tác nhân',
|
||||
|
||||
@@ -663,6 +663,7 @@ export default {
|
||||
'language.en': 'English',
|
||||
'language.de': 'Deutsch',
|
||||
'language.vi': 'Tiếng Việt',
|
||||
'language.ptBR': 'Português (Brasil)',
|
||||
|
||||
// 首页文本
|
||||
'home.addAgent': '添加智能体',
|
||||
|
||||
@@ -663,6 +663,7 @@ export default {
|
||||
'language.en': 'English',
|
||||
'language.de': 'Deutsch',
|
||||
'language.vi': 'Tiếng Việt',
|
||||
'language.ptBR': 'Português (Brasil)',
|
||||
|
||||
// 首頁文本
|
||||
'home.addAgent': '添加智能體',
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
<el-dropdown-item @click.native="changeLanguage('vi')">
|
||||
{{ $t("language.vi") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="changeLanguage('pt_BR')">
|
||||
{{ $t("language.ptBR") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
@@ -188,6 +191,8 @@ export default {
|
||||
return this.$t("language.de");
|
||||
case "vi":
|
||||
return this.$t("language.vi");
|
||||
case "pt_BR":
|
||||
return this.$t("language.ptBR");
|
||||
default:
|
||||
return this.$t("language.zhCN");
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@ export default {
|
||||
return require("@/assets/xiaozhi-ai_de.png");
|
||||
case "vi":
|
||||
return require("@/assets/xiaozhi-ai_vi.png");
|
||||
case "pt_BR":
|
||||
return require("@/assets/xiaozhi-ai_en.png");
|
||||
default:
|
||||
return require("@/assets/xiaozhi-ai.png");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(tree:*)",
|
||||
"Bash(find:*)"
|
||||
"Bash(find:*)",
|
||||
"Bash(python3:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -1118,6 +1118,14 @@ class ConnectionHandler:
|
||||
async def close(self, ws=None):
|
||||
"""资源清理方法"""
|
||||
try:
|
||||
# 清理 VAD 连接资源
|
||||
if (
|
||||
hasattr(self, "vad")
|
||||
and self.vad
|
||||
and hasattr(self.vad, "release_conn_resources")
|
||||
):
|
||||
self.vad.release_conn_resources(self)
|
||||
|
||||
# 清理音频缓冲区
|
||||
if hasattr(self, "audio_buffer"):
|
||||
self.audio_buffer.clear()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import time
|
||||
import os
|
||||
import numpy as np
|
||||
import torch
|
||||
import opuslib_next
|
||||
import onnxruntime
|
||||
from config.logger import setup_logging
|
||||
from core.providers.vad.base import VADProviderBase
|
||||
|
||||
@@ -12,16 +13,17 @@ logger = setup_logging()
|
||||
class VADProvider(VADProviderBase):
|
||||
def __init__(self, config):
|
||||
logger.bind(tag=TAG).info("SileroVAD", config)
|
||||
self.model, _ = torch.hub.load(
|
||||
repo_or_dir=config["model_dir"],
|
||||
source="local",
|
||||
model="silero_vad",
|
||||
force_reload=False,
|
||||
|
||||
model_path = os.path.join(
|
||||
config["model_dir"], "src", "silero_vad", "data", "silero_vad.onnx"
|
||||
)
|
||||
opts = onnxruntime.SessionOptions()
|
||||
opts.inter_op_num_threads = 1
|
||||
opts.intra_op_num_threads = 1
|
||||
self.session = onnxruntime.InferenceSession(
|
||||
model_path, providers=["CPUExecutionProvider"], sess_options=opts
|
||||
)
|
||||
|
||||
self.decoder = opuslib_next.Decoder(16000, 1)
|
||||
|
||||
# 处理空字符串的情况
|
||||
threshold = config.get("threshold", "0.5")
|
||||
threshold_low = config.get("threshold_low", "0.2")
|
||||
min_silence_duration_ms = config.get("min_silence_duration_ms", "1000")
|
||||
@@ -33,40 +35,58 @@ class VADProvider(VADProviderBase):
|
||||
int(min_silence_duration_ms) if min_silence_duration_ms else 1000
|
||||
)
|
||||
|
||||
# 至少要多少帧才算有语音
|
||||
self.frame_window_threshold = 3
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, 'decoder') and self.decoder is not None:
|
||||
try:
|
||||
del self.decoder
|
||||
except Exception:
|
||||
pass
|
||||
def _init_connection_state(self, conn):
|
||||
"""为连接初始化独立的 VAD 状态"""
|
||||
if not hasattr(conn, "_vad_opus_decoder"):
|
||||
conn._vad_opus_decoder = opuslib_next.Decoder(16000, 1)
|
||||
if not hasattr(conn, "_vad_state"):
|
||||
conn._vad_state = np.zeros((2, 1, 128), dtype=np.float32)
|
||||
if not hasattr(conn, "_vad_context"):
|
||||
conn._vad_context = np.zeros((1, 64), dtype=np.float32)
|
||||
|
||||
def release_conn_resources(self, conn):
|
||||
"""释放连接的 VAD 资源(连接关闭时调用)"""
|
||||
for attr in ("_vad_opus_decoder", "_vad_state", "_vad_context"):
|
||||
if hasattr(conn, attr):
|
||||
try:
|
||||
delattr(conn, attr)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def is_vad(self, conn, opus_packet):
|
||||
# 手动模式:直接返回True,不进行实时VAD检测,所有音频都缓存
|
||||
if conn.client_listen_mode == "manual":
|
||||
return True
|
||||
|
||||
try:
|
||||
pcm_frame = self.decoder.decode(opus_packet, 960)
|
||||
conn.client_audio_buffer.extend(pcm_frame) # 将新数据加入缓冲区
|
||||
|
||||
# 处理缓冲区中的完整帧(每次处理512采样点)
|
||||
try:
|
||||
self._init_connection_state(conn)
|
||||
|
||||
pcm_frame = conn._vad_opus_decoder.decode(opus_packet, 960)
|
||||
conn.client_audio_buffer.extend(pcm_frame)
|
||||
|
||||
client_have_voice = False
|
||||
while len(conn.client_audio_buffer) >= 512 * 2:
|
||||
# 提取前512个采样点(1024字节)
|
||||
chunk = conn.client_audio_buffer[: 512 * 2]
|
||||
conn.client_audio_buffer = conn.client_audio_buffer[512 * 2 :]
|
||||
|
||||
# 转换为模型需要的张量格式
|
||||
audio_int16 = np.frombuffer(chunk, dtype=np.int16)
|
||||
audio_float32 = audio_int16.astype(np.float32) / 32768.0
|
||||
audio_tensor = torch.from_numpy(audio_float32)
|
||||
audio_input = np.concatenate(
|
||||
[conn._vad_context, audio_float32.reshape(1, -1)], axis=1
|
||||
).astype(np.float32)
|
||||
|
||||
# 检测语音活动
|
||||
with torch.no_grad():
|
||||
speech_prob = self.model(audio_tensor, 16000).item()
|
||||
ort_inputs = {
|
||||
"input": audio_input,
|
||||
"state": conn._vad_state,
|
||||
"sr": np.array(16000, dtype=np.int64),
|
||||
}
|
||||
out, state = self.session.run(None, ort_inputs)
|
||||
|
||||
conn._vad_state = state
|
||||
conn._vad_context = audio_input[:, -64:]
|
||||
speech_prob = out.item()
|
||||
|
||||
# 双阈值判断
|
||||
if speech_prob >= self.vad_threshold:
|
||||
|
||||
@@ -1201,6 +1201,113 @@ body {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.chat-stream::-webkit-scrollbar,
|
||||
.modal-body::-webkit-scrollbar,
|
||||
.mcp-tools-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-track,
|
||||
.modal-body::-webkit-scrollbar-track,
|
||||
.mcp-tools-list::-webkit-scrollbar-track {
|
||||
background: #2f3136;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-thumb,
|
||||
.modal-body::-webkit-scrollbar-thumb,
|
||||
.mcp-tools-list::-webkit-scrollbar-thumb {
|
||||
background: #40444b;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-thumb:hover,
|
||||
.modal-body::-webkit-scrollbar-thumb:hover,
|
||||
.mcp-tools-list::-webkit-scrollbar-thumb:hover {
|
||||
background: #4f545c;
|
||||
}
|
||||
|
||||
/* ==================== 摄像头显示区域样式 ==================== */
|
||||
.camera-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
width: 180px;
|
||||
height: 240px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.camera-container.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.camera-container.dragging {
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(88, 101, 242, 0.5);
|
||||
}
|
||||
|
||||
@keyframes flipWithPosition {
|
||||
0% {
|
||||
transform: var(--original-transform) rotateY(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: var(--original-transform) rotateY(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.camera-container.flip {
|
||||
animation: flipWithPosition 0.5s ease-in-out 1 forwards;
|
||||
}
|
||||
|
||||
#cameraVideo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
background: #1a1a1a;
|
||||
pointer-events: none;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.camera-switch-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.camera-switch-container {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.camera-switch {
|
||||
display: none;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.camera-switch .btn-icon {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.camera-switch.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ==================== 响应式设计 ==================== */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
@@ -1264,6 +1371,19 @@ body {
|
||||
left: 10px;
|
||||
bottom: 80px;
|
||||
}
|
||||
|
||||
.chat-ipt input {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.camera-container {
|
||||
width: 130px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
@@ -1343,68 +1463,4 @@ body {
|
||||
width: 250px;
|
||||
height: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.chat-stream::-webkit-scrollbar,
|
||||
.modal-body::-webkit-scrollbar,
|
||||
.mcp-tools-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-track,
|
||||
.modal-body::-webkit-scrollbar-track,
|
||||
.mcp-tools-list::-webkit-scrollbar-track {
|
||||
background: #2f3136;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-thumb,
|
||||
.modal-body::-webkit-scrollbar-thumb,
|
||||
.mcp-tools-list::-webkit-scrollbar-thumb {
|
||||
background: #40444b;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-stream::-webkit-scrollbar-thumb:hover,
|
||||
.modal-body::-webkit-scrollbar-thumb:hover,
|
||||
.mcp-tools-list::-webkit-scrollbar-thumb:hover {
|
||||
background: #4f545c;
|
||||
}
|
||||
|
||||
/* ==================== 摄像头显示区域样式 ==================== */
|
||||
.camera-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.camera-container.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.camera-container.dragging {
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
|
||||
border-color: rgba(88, 101, 242, 0.5);
|
||||
}
|
||||
|
||||
#cameraVideo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
background: #1a1a1a;
|
||||
pointer-events: none;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
@@ -25,6 +25,7 @@ class App {
|
||||
this.audioPlayer = null;
|
||||
this.live2dManager = null;
|
||||
this.cameraStream = null;
|
||||
this.currentFacingMode = 'user';
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
@@ -127,6 +128,9 @@ class App {
|
||||
async initCamera() {
|
||||
const cameraContainer = document.getElementById('cameraContainer');
|
||||
const cameraVideo = document.getElementById('cameraVideo');
|
||||
const cameraSwitch = document.getElementById('cameraSwitch');
|
||||
const cameraSwitchMask = document.getElementById('cameraSwitchMask');
|
||||
const dialBtn = document.getElementById('dialBtn');
|
||||
|
||||
if (!cameraContainer || !cameraVideo) {
|
||||
log('摄像头元素未找到,跳过初始化', 'warning');
|
||||
@@ -188,11 +192,24 @@ class App {
|
||||
}
|
||||
log('正在请求摄像头权限...', 'info');
|
||||
this.cameraStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { width: 320, height: 240, facingMode: 'user' },
|
||||
video: { width: 320, height: 240, facingMode: this.currentFacingMode },
|
||||
audio: false
|
||||
});
|
||||
cameraVideo.srcObject = this.cameraStream;
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
||||
if (videoDevices.length > 1) {
|
||||
if (cameraSwitch) cameraSwitch.classList.add('active');
|
||||
}
|
||||
cameraContainer.classList.add('active');
|
||||
|
||||
// 切换时挂断情况
|
||||
const hasActive = dialBtn.classList.contains('dial-active');
|
||||
if (!hasActive) {
|
||||
cameraContainer.classList.remove('active');
|
||||
cameraSwitch.classList.remove('active');
|
||||
window.stopCamera();
|
||||
}
|
||||
log('摄像头已启动', 'success');
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -217,6 +234,32 @@ class App {
|
||||
}
|
||||
};
|
||||
|
||||
window.switchCamera = async() => {
|
||||
if (window.switchCameraTimer) return;
|
||||
if (this.cameraStream) {
|
||||
const currentTransform = window.getComputedStyle(cameraContainer).transform;
|
||||
const originalTransform = currentTransform === 'none' ? 'translate(0px, 0px)' : currentTransform;
|
||||
cameraContainer.style.setProperty('--original-transform', originalTransform);
|
||||
cameraContainer.classList.add('flip');
|
||||
if (cameraSwitchMask) cameraSwitchMask.style.opacity = 0;
|
||||
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
|
||||
window.stopCamera();
|
||||
window.startCamera();
|
||||
|
||||
window.switchCameraTimer = setTimeout(() => {
|
||||
if (this.currentFacingMode === 'user') {
|
||||
cameraVideo.style.transform = 'scaleX(-1)';
|
||||
} else {
|
||||
cameraVideo.style.transform = 'scaleX(1)';
|
||||
}
|
||||
window.switchCameraTimer = null;
|
||||
cameraContainer.classList.remove('flip');
|
||||
cameraContainer.style.removeProperty('--original-transform');
|
||||
if (cameraSwitchMask) cameraSwitchMask.style.opacity = 1;
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
window.takePhoto = (question = '描述一下看到的物品') => {
|
||||
return new Promise(async (resolve) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
|
||||
@@ -98,6 +98,18 @@ class UIController {
|
||||
});
|
||||
}
|
||||
|
||||
// Camera switch button
|
||||
const cameraSwitch = document.getElementById('cameraSwitch');
|
||||
const cameraSwitchMask = document.getElementById('cameraSwitchMask');
|
||||
if (cameraSwitchMask) {
|
||||
cameraSwitchMask.addEventListener('click', () => {
|
||||
const isCameraActive = cameraSwitch.classList.contains('active');
|
||||
if (isCameraActive) {
|
||||
window.switchCamera();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Dial button
|
||||
const dialBtn = document.getElementById('dialBtn');
|
||||
if (dialBtn) {
|
||||
@@ -115,6 +127,7 @@ class UIController {
|
||||
if (isConnected) {
|
||||
wsHandler.disconnect();
|
||||
this.updateDialButton(false);
|
||||
if (cameraSwitch) cameraSwitch.classList.remove('active');
|
||||
this.addChatMessage('Disconnected, see you next time~😊', false);
|
||||
} else {
|
||||
// Check if OTA URL is filled
|
||||
@@ -153,6 +166,7 @@ class UIController {
|
||||
if (isActive) {
|
||||
// 关闭摄像头
|
||||
if (typeof window.stopCamera === 'function') {
|
||||
if (cameraSwitch) cameraSwitch.classList.remove('active');
|
||||
window.stopCamera();
|
||||
}
|
||||
cameraContainer.classList.remove('active');
|
||||
|
||||
@@ -44,9 +44,18 @@
|
||||
|
||||
<!-- 主容器 -->
|
||||
<div class="container">
|
||||
|
||||
<!-- 摄像头显示区域(可拖动) -->
|
||||
<div class="camera-container" id="cameraContainer">
|
||||
<video id="cameraVideo" autoplay playsinline muted></video>
|
||||
<div class="camera-switch-mask" id="cameraSwitchMask">
|
||||
<div class="camera-switch-container">
|
||||
<!-- 摄像头切换按钮 -->
|
||||
<div class="camera-switch" id="cameraSwitch">
|
||||
<svg class="btn-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7319" width="32" height="32"><path d="M722.1 530.3c0-44.9-13.8-86.5-37.2-120.9-0.3-0.6-0.6-1.2-0.9-1.7-2.9-4.2-6.1-8.1-9.2-12-0.4-0.5-0.7-1-1.1-1.5-21.4-26.3-48.3-46.7-78.7-59.9-0.8-0.4-1.7-0.8-2.5-1.2-4.9-2-9.8-3.8-14.9-5.5-1.8-0.6-3.6-1.3-5.4-1.8-4.4-1.3-8.8-2.4-13.3-3.5-2.5-0.6-4.9-1.2-7.4-1.7-1.2-0.2-2.4-0.6-3.6-0.8-3.3-0.6-6.7-0.8-10-1.3-2.3-0.3-4.6-0.7-6.9-0.9-5.6-0.5-11.2-0.8-16.7-0.9-1 0-2-0.1-3-0.1-0.2 0-0.3 0.1-0.5 0.1-42.8 0-84.6 13.1-120.4 38.6-11.5 8.1-14.2 24.1-6.2 35.7 8 11.6 23.8 14.4 35.2 6.3 27.7-19.7 60-29.6 93.1-29.3 4.8 0 9.5 0.3 14.1 0.7 1.4 0.2 2.8 0.4 4.3 0.5 3.8 0.5 7.6 1 11.3 1.8 1.6 0.3 3.3 0.8 4.9 1.1 3.7 0.8 7.3 1.7 10.9 2.8 1.1 0.4 2.2 0.8 3.4 1.2 4.1 1.3 8.1 2.8 12 4.5 0.4 0.2 0.8 0.4 1.2 0.5 23.6 10.3 44.3 26.1 60.4 45.9 0.1 0.1 0.2 0.3 0.3 0.4 22.7 28 36.4 63.9 36.4 102.9h-42.2L697 632.9l67.5-102.6h-42.4z m-119 133c-27.9 19.8-60.5 29.7-93.9 29.3-4.4-0.1-8.8-0.3-13.1-0.7-1.8-0.2-3.5-0.5-5.3-0.7-3.4-0.5-6.8-0.9-10.1-1.6-2-0.4-4-0.9-6.1-1.4-3.3-0.8-6.5-1.5-9.7-2.5-1.5-0.5-3-1-4.6-1.5-3.7-1.2-7.3-2.5-10.8-4-0.8-0.3-1.6-0.7-2.4-1.1-4.1-1.9-8.2-3.8-12.2-6l-0.6-0.3c-13.4-7.5-25.7-16.8-36.4-27.7-0.2-0.2-0.3-0.4-0.5-0.6-3.3-3.4-6.5-6.9-9.5-10.7-0.6-0.8-1.3-1.6-1.9-2.5-21.9-27.8-35.1-63-35.1-101.2h42.2l-67.5-102.6-67.5 102.6h42.2c0 45 13.9 86.7 37.3 121.1 0.3 0.5 0.5 1 0.8 1.5 2.4 3.5 5.1 6.8 7.7 10.1 1 1.3 1.9 2.6 2.9 3.8 3.9 4.7 7.9 9.2 12.1 13.5 0.4 0.4 0.8 0.9 1.2 1.3 14.1 14.3 30.1 26.4 47.4 36 0.5 0.3 0.9 0.6 1.4 0.8 5 2.7 10.1 5.2 15.3 7.5l3.9 1.8c4.4 1.9 9 3.5 13.6 5.1 2.2 0.7 4.4 1.5 6.6 2.2 4 1.2 8.1 2.2 12.3 3.2 2.8 0.7 5.5 1.4 8.3 1.9 1.2 0.3 2.3 0.6 3.4 0.8 3.9 0.7 7.8 1.1 11.8 1.6l4.2 0.6c7 0.7 14.1 1.2 21.1 1.2 42.9 0 84.7-13.3 120.6-38.8 11.4-8.2 14.2-24.1 6.2-35.8-8-11.5-23.8-14.3-35.3-6.2z m271.3-421.6H763c-26.5 0-82.9-119.7-112.5-119.7H370.6c-29.6 0-85.9 119.7-112.4 119.7H147.5c-46.3 0-83.9 40.2-83.9 89.7v478.8c0 49.6 37.6 89.8 83.9 89.8h726.9c46.3 0 83.9-40.1 83.9-89.8V331.4c0-49.5-37.6-89.7-83.9-89.7z m28 538.6c0 33.1-25 59.8-55.9 59.8h-671c-30.9 0-55.9-26.8-55.9-59.8V361.4c0-33 25-59.8 55.9-59.8h111.2c25.8 0 82-119.7 111.6-119.7h223.2c29.5 0 85.6 119.7 111.5 119.7h113.5c30.9 0 55.9 26.8 55.9 59.8v418.9z" p-id="7320" fill="#ffffff"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接状态指示器 - 页面顶部中部 -->
|
||||
|
||||
Reference in New Issue
Block a user