From afa469c08ce2e4ec34358948e96d1c87c0c23dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AC=A3=E5=8D=97=E7=A7=91=E6=8A=80?= Date: Thu, 20 Feb 2025 22:40:41 +0800 Subject: [PATCH] Home assistant (#97) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add home assistant * update home assistant remark * update config * update:优化文档 --------- Co-authored-by: ck3 Co-authored-by: Kyrie Chen <794103684@qq.com> Co-authored-by: hrz <1710360675@qq.com> --- README.md | 46 +++++++++----- config.yaml | 6 ++ .../llm/homeassistant/homeassistant.py | 62 +++++++++++++++++++ core/providers/tts/fishspeech.py | 8 ++- core/providers/tts/siliconflow.py | 2 - requirements.txt | 3 +- 6 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 core/providers/llm/homeassistant/homeassistant.py diff --git a/README.md b/README.md index e29c35cf..c77bd07f 100644 --- a/README.md +++ b/README.md @@ -80,15 +80,16 @@ server: ### LLM -| 类型 | 平台名称 | 使用方式 | 收费模式 | 备注 | -|:---:|:------------------:|:-----------:|:--------:|:-----------------------------------------------------------------:| -| LLM | 阿里百炼 (AliLLM) | openai 接口调用 | 消耗 token | [点击申请密钥](https://bailian.console.aliyun.com/?apiKey=1#/api-key) | -| LLM | 深度求索 (DeepSeekLLM) | openai 接口调用 | 消耗 token | [点击申请密钥](https://platform.deepseek.com/) | -| LLM | 智谱(ChatGLMLLM) | openai 接口调用 | 免费 | 虽然免费,仍需[点击申请密钥](https://bigmodel.cn/usercenter/proj-mgmt/apikeys) | -| LLM | OllamaLLM | ollama 接口调用 | 免费/自定义 | 需预先下载模型(`ollama pull`),服务地址:`http://localhost:11434` | -| LLM | DifyLLM | dify 接口调用 | 消耗 token | 本地化部署,注意配置提示词需在 Dify 控制台设置 | -| LLM | GeminiLLM | gemini 接口调用 | 免费 | [点击申请密钥](https://aistudio.google.com/apikey) | -| LLM | CozeLLM | coze 接口调用 | 消耗 token | 需提供 bot_id、user_id 及个人令牌 | +| 类型 | 平台名称 | 使用方式 | 收费模式 | 备注 | +|:---:|:------------------:|:---------------------:|:--------:|:-----------------------------------------------------------------:| +| LLM | 阿里百炼 (AliLLM) | openai 接口调用 | 消耗 token | [点击申请密钥](https://bailian.console.aliyun.com/?apiKey=1#/api-key) | +| LLM | 深度求索 (DeepSeekLLM) | openai 接口调用 | 消耗 token | [点击申请密钥](https://platform.deepseek.com/) | +| LLM | 智谱(ChatGLMLLM) | openai 接口调用 | 免费 | 虽然免费,仍需[点击申请密钥](https://bigmodel.cn/usercenter/proj-mgmt/apikeys) | +| LLM | OllamaLLM | ollama 接口调用 | 免费/自定义 | 需预先下载模型(`ollama pull`),服务地址:`http://localhost:11434` | +| LLM | DifyLLM | dify 接口调用 | 消耗 token | 本地化部署,注意配置提示词需在 Dify 控制台设置 | +| LLM | GeminiLLM | gemini 接口调用 | 免费 | [点击申请密钥](https://aistudio.google.com/apikey) | +| LLM | CozeLLM | coze 接口调用 | 消耗 token | 需提供 bot_id、user_id 及个人令牌 | +| LLM | Home Assistant | homeassistant语音助手接口调用 | 免费 | 需提供home assistant令牌 | 实际上,任何支持 openai 接口调用的 LLM 均可接入使用。 @@ -127,11 +128,8 @@ server: ### 一、[部署文档](./docs/Deployment.md) -1. - * - -*[本地源码运行](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%89%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C) -** +1.**[本地源码运行](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%89%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C)** + 适合熟悉 Conda 环境或希望从零搭建运行环境的用户。 对于对响应速度要求较高的场景,推荐使用本地源码运行方式以降低额外开销。 @@ -152,7 +150,7 @@ server: ### 2、我想通过小智控制电灯、空调、远程开关机等操作 💡 -建议:在配置文件中将 `LLM` 设置为 `DifyLLM`,并通过 `Dify` 编排智能体来实现相关控制。 +建议:在配置文件中将 `LLM` 设置为 `HomeAssistant`,通过 调用`HomeAssistant`接口实现相关控制。 ### 3、我说话很慢,停顿时小智老是抢话 🗣️ @@ -166,7 +164,21 @@ VAD: min_silence_duration_ms: 700 # 如果说话停顿较长,可将此值调大 ``` -### 4、如何提高小智对话响应速度? ⚡ +### 4、为什么我说的话,小智识别出来很多韩文、日文、英文?🇰🇷 + +建议:检查一下`models/SenseVoiceSmall`是否已经有`model.pt`文件,如果没有就要下载,查看这里[下载语音识别模型文件](docs/Deployment.md#模型文件) + +### 5、为什么会出现“TTS 任务出错 文件不存在”?📁 + +建议:检查一下是否正确使用`conda` 安装了`libopus`和`ffmpeg`库。 + +如果没有安装,就安装 +``` +conda install conda-forge::libopus +conda install conda-forge::ffmpeg +``` + +### 6、如何提高小智对话响应速度? ⚡ 本项目默认配置为低成本方案,建议初学者先使用默认免费模型,解决“跑得动”的问题,再优化“跑得快”。 如需提升响应速度,可尝试更换各组件。以下为各组件的响应速度测试数据(仅供参考,不构成承诺): @@ -224,7 +236,7 @@ TTS 性能排行: - LLM:`AliLLM` - TTS:`DoubaoTTS` -### 5、更多问题,可联系我们反馈 💬 +### 7、更多问题,可联系我们反馈 💬 ![图片](docs/images/wechat.jpg) diff --git a/config.yaml b/config.yaml index bf4eea01..a0ce2e1b 100644 --- a/config.yaml +++ b/config.yaml @@ -128,6 +128,12 @@ LLM: user_id: 你的user_id base_url: "https://api.coze.cn/open_api/v2/chat" # 服务地址 personal_access_token: 你的coze个人令牌 + HomeAssistant: + # 定义LLM API类型 + type: homeassistant + base_url: http://homeassistant.local:8123 + agent_id: conversation.chatgpt + api_key: 你的home assistant api访问令牌 TTS: # 当前支持的type为edge、doubao,可自行适配 EdgeTTS: diff --git a/core/providers/llm/homeassistant/homeassistant.py b/core/providers/llm/homeassistant/homeassistant.py new file mode 100644 index 00000000..115e85e9 --- /dev/null +++ b/core/providers/llm/homeassistant/homeassistant.py @@ -0,0 +1,62 @@ +import requests +from requests.exceptions import RequestException +from config.logger import setup_logging +from core.providers.llm.base import LLMProviderBase + +TAG = __name__ +logger = setup_logging() + + +class LLMProvider(LLMProviderBase): + def __init__(self, config): + self.agent_id = config.get("agent_id") # 对应 agent_id + self.api_key = config.get("api_key") + self.base_url = config.get("base_url", config.get("url")) # 默认使用 base_url + self.api_url = f"{self.base_url}/api/conversation/process" # 拼接完整的 API URL + + def response(self, session_id, dialogue): + print(dialogue) + try: + # home assistant语音助手自带意图,无需使用xiaozhi ai自带的,只需要把用户说的话传递给home assistant即可 + + # 提取最后一个 role 为 'user' 的 content + input_text = None + if isinstance(dialogue, list): # 确保 dialogue 是一个列表 + # 逆序遍历,找到最后一个 role 为 'user' 的消息 + for message in reversed(dialogue): + if message.get("role") == "user": # 找到 role 为 'user' 的消息 + input_text = message.get("content", "") + break # 找到后立即退出循环 + + # 构造请求数据 + payload = { + "text": input_text, + "agent_id": self.agent_id, + "conversation_id": session_id # 使用 session_id 作为 conversation_id + } + # 设置请求头 + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + # 发起 POST 请求 + response = requests.post(self.api_url, json=payload, headers=headers) + + # 检查请求是否成功 + response.raise_for_status() + + # 解析返回数据 + data = response.json() + speech = data.get("response", {}).get("speech", {}).get("plain", {}).get("speech", "") + + # 返回生成的内容 + if speech: + yield speech + else: + logger.bind(tag=TAG).warning("API 返回数据中没有 speech 内容") + + except RequestException as e: + logger.bind(tag=TAG).error(f"HTTP 请求错误: {e}") + except Exception as e: + logger.bind(tag=TAG).error(f"生成响应时出错: {e}") diff --git a/core/providers/tts/fishspeech.py b/core/providers/tts/fishspeech.py index c7121498..1cbe0810 100644 --- a/core/providers/tts/fishspeech.py +++ b/core/providers/tts/fishspeech.py @@ -9,8 +9,11 @@ from pydantic import BaseModel, Field, conint, model_validator from typing_extensions import Annotated from datetime import datetime from typing import Literal -# from base import TTSProviderBase from core.providers.tts.base import TTSProviderBase +from config.logger import setup_logging + +TAG = __name__ +logger = setup_logging() class ServeReferenceAudio(BaseModel): @@ -86,6 +89,9 @@ class TTSProvider(TTSProviderBase): self.channels = config.get("channels",1) self.rate = config.get("rate",44100) self.api_key = config.get("api_key","YOUR_API_KEY") + if not self.api_key or "你" in self.api_key: + logger.bind(tag=TAG).error("你还没配置FishSpeech TTS的密钥,请在配置文件中配置密钥,否则无法正常工作") + return self.normalize = config.get("normalize",True) self.max_new_tokens = config.get("max_new_tokens",1024) self.chunk_length = config.get("chunk_length",200) diff --git a/core/providers/tts/siliconflow.py b/core/providers/tts/siliconflow.py index faca4405..4c0cc356 100644 --- a/core/providers/tts/siliconflow.py +++ b/core/providers/tts/siliconflow.py @@ -1,7 +1,5 @@ import os import uuid -import json -import base64 import requests from datetime import datetime from core.providers.tts.base import TTSProviderBase diff --git a/requirements.txt b/requirements.txt index be566306..bfa9ed80 100755 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ aiohttp==3.9.3 aiohttp_cors==0.7.0 ormsgpack==1.7.0 ruamel.yaml==0.18.10 -loguru==0.7.3 \ No newline at end of file +loguru==0.7.3 +requests>=2.0.0 \ No newline at end of file