Compare commits

...

66 Commits
v4.0.0 ... main

Author SHA1 Message Date
Del Wang
1aea52ef27
docs: 新增项目归档公告 2026-04-04 15:53:38 +08:00
fuchao
94ca31eb7e Update compatibility.md
增加一个丐版的小米音响:Xiaomi 智能音响

已完成接入测试,可完美运行。

重点 review `playingCommand`  我看 Pro 也是支持 [3,1,1]的,为啥没写上呢~
2025-09-10 12:19:01 +08:00
Del
5d5937d208
docs: 更新 migpt-next 和 open-xiaoai 项目链接 2025-05-21 23:58:11 +08:00
guwunianzhi
6ca6a7e324
docs: 更新 Xiaomi 智能家庭屏 Pro 8 参数
* Update compatibility.md

名称:Xiaomi 智能家庭屏 Pro 8
型号:X8F
ttsCommand:[7, 3]
wakeUpCommand:[7, 1]
playingCommand:未设置
streamResponse:false(支持连续对话)

* docs: update compatibility.md

---------

Co-authored-by: Del <35302658+idootop@users.noreply.github.com>
2025-03-21 23:46:38 +08:00
Del Wang
eaadc49b08
docs: 更新 Xiaomi 智能音箱 Pro 配置参数 2025-03-01 11:05:33 +08:00
Del Wang
33abacd596
docs: update readme 2025-02-12 21:49:31 +08:00
Jason
f91b88d39d
docs: add mi-gpt-docker-easy-setup.md 2025-02-12 21:37:10 +08:00
Del Wang
1d50c27947
docs: 新增 Xiaomi 智能家庭屏 Mini 配置 2025-02-11 17:30:39 +08:00
Del
21def7b471
Merge pull request #246 from czy0538/main
Supports online search for the Qwen series models.
2025-01-31 11:03:21 +08:00
Zhiyuan Cao
79787e8246 Supports online search for the Qwen series models. 2025-01-30 11:18:47 +08:00
Del
367e4caef0
chore: update default openai model to gpt-4o-mini 2024-12-28 16:28:03 +08:00
Del Wang
2eb62f2cc9
docs: update readme 2024-12-21 14:42:16 +08:00
Del
d676de0509
Merge pull request #223 from LipapaSma/main 2024-12-21 14:37:09 +08:00
吴翔
a36ed3f494 新增migpt 配置中心地址推荐 2024-12-21 14:26:35 +08:00
Del
b117245429
docs: update readme 2024-12-03 21:12:35 +08:00
Del
29e2bc95cd
docs: add docker pulls badge 2024-11-24 08:26:59 +08:00
WJG
5d033c0170
docs: update sponsors 2024-11-16 20:41:05 +08:00
WJG
54fbd21205
docs: 更新 FAQ 2024-10-19 20:57:50 +08:00
Del
9f83eb1a0b
Merge pull request #178 from idootop/dev
release: v4.2.0
2024-08-26 21:46:31 +08:00
WJG
a1b04816dd
feat: 新增对小爱音箱 LLM 消息的支持 2024-08-26 21:43:54 +08:00
WJG
d14ce3bfaf
chore: 移除历史测试文件 2024-08-26 21:33:54 +08:00
Del
5d455e2ffe
Merge pull request #160 from yanyao2333/fix-code-block-format
将AI返回内容中的markdown代码块转换为普通json
2024-07-24 15:18:59 +08:00
roitium
bba886e275 fix: 恢复原threshold数值 2024-07-24 10:21:44 +08:00
roitium
11dc25aa60 refactor: 调用封装后函数 2024-07-24 10:18:57 +08:00
roitium
208b2db60b fix: 将json清洗操作封装为函数
refactor: 将json编解码操作放入/utils/parse
2024-07-24 10:18:24 +08:00
roitium
c985e5b08f fix: 去除掉ai返回内容中的代码块样式 2024-07-22 12:13:02 +08:00
roitium
2f53d99359 fix: 增加记忆生成失败时的日志 2024-07-22 12:06:50 +08:00
WJG
27bde0ca4f
docs: 添加 Xiaomi Sound Pro 2024-07-07 10:43:40 +08:00
WJG
ac310a2e16
docs: update changelog 2024-07-01 22:34:24 +08:00
Del
02d5cd6fe2
docs: 更新 readme 首页排版 2024-07-01 21:10:48 +08:00
WJG
619df40d42
docs: 更新 pdf 文件教程链接 2024-07-01 20:14:53 +08:00
WJG
1145048bc9
docs: 添加视频教程 2024-07-01 19:58:22 +08:00
WJG
a0f1750b98
refactor: 使用 proxy-agent 替换 https-proxy-agent 和 socks-proxy-agent 2024-06-30 18:34:17 +08:00
WJG
45ca981038
docs: 添加小爱智能家庭屏 10 2024-06-26 19:53:15 +08:00
WJG
1362773c13
docs: 添加小爱音箱转义请求参数的相关说明 2024-06-25 09:12:46 +08:00
WJG
500cb3be81
docs: 修复 TTS 演示请求路径 2024-06-25 09:10:45 +08:00
WJG
08f3888cd9
docs: 更新教程链接 2024-06-24 22:15:53 +08:00
WJG
adfdcc4ee5
docs: 更新推荐教程列表 2024-06-24 22:12:11 +08:00
Del
0634ee7fb7
Merge pull request #125 from lmk123/patch-1
docs: 添加推荐项目 migpt-cli
2024-06-23 23:22:33 +08:00
Del
d890cdc855
Merge pull request #124 from mxyblog/main
docs: 更新 pdf 制作者联系方式
2024-06-23 23:21:04 +08:00
lmk123
814182c1c5
docs: 添加推荐项目 2024-06-23 22:27:36 +08:00
mxyblog
dfb48a2025
更新pdf制作者联系方式 2024-06-23 20:13:09 +08:00
WJG
e463cc9655
docs: 更新 pdf 文件链接 2024-06-23 19:47:57 +08:00
WJG
bbdb80e9bc
docs: 添加本地开发 db 初始化失败的说明 2024-06-23 19:45:31 +08:00
WJG
4656e426cd
docs: 添加项目与教程推荐 2024-06-23 19:42:17 +08:00
WJG
f7683a6ec7
docs: 添加小爱触屏音箱 2024-06-23 19:08:04 +08:00
WJG
62248b04cd
docs: 更新 v4.1.0 贡献者列表 2024-06-23 08:08:25 +08:00
Del
a8c9ba7e0f
Merge pull request #123 from idootop/dev
release: v4.1.0
2024-06-23 00:39:08 +08:00
WJG
ee2c018278
release: v4.1.0 2024-06-23 00:32:21 +08:00
WJG
5c6f61fce3
docs: 添加系统 Prompt 常见问题解答 2024-06-23 00:23:18 +08:00
WJG
7224e3fb08
docs: 添加 TTS 和 OpenAI baseURL 示例和注意事项 2024-06-23 00:00:20 +08:00
WJG
fc5b320141
docs: 添加 timeout 参数说明 2024-06-22 23:27:08 +08:00
WJG
cf9c7865cf
docs: 添加 server 端异地登录失败,使用本地登录凭证的教程 2024-06-22 23:25:25 +08:00
WJG
314b0f3244
docs: 添加连续对话下和小爱音箱说话没有反应的说明 2024-06-22 22:53:30 +08:00
WJG
e888ff451e
docs: 新增如何提高 AI 回答反应速度的配置教程 2024-06-22 21:57:18 +08:00
WJG
3c5b7e6bd7
docs: 添加召唤 AI 回答问题的唤醒指令的说明 2024-06-22 21:34:36 +08:00
WJG
beafec6d11
fix: 修复部分机型连续对话异常的问题(比如小爱音箱 Play) 2024-06-22 20:19:24 +08:00
WJG
77d31a024a
docs: 添加小爱音箱 Art 2024-06-21 23:22:57 +08:00
WJG
9d8b2875c0
fix: 更正默认 botProfile 2024-06-20 21:53:21 +08:00
WJG
6e73ce8eee
feat: 允许通过设置 systemTemplate 为空字符串来关闭系统消息 2024-06-20 21:47:20 +08:00
WJG
84a46b688b
chore: 本地测试 MiGPT 时从配置文件加载参数 2024-06-19 23:02:43 +08:00
WJG
02114694ea
docs: 优化关闭流式响应时不能使用对话模式的提示语 2024-06-19 21:33:34 +08:00
WJG
974fbbf653
chore: speaker is undefined 2024-06-19 21:22:21 +08:00
WJG
c033235d2c
docs: 优化切换音色指令示例 2024-06-19 20:28:05 +08:00
WJG
84e039cc76
chore: 默认网络请求超时上调为 5 秒 2024-06-19 20:27:32 +08:00
WJG
8d48ee5b86
docs: 更新 TTS 参数文档 2024-06-18 20:25:24 +08:00
38 changed files with 647 additions and 543 deletions

View File

@ -1,7 +1,8 @@
# OpenAI也支持通义千问、MoonShot、DeepSeek 等模型)
OPENAI_MODEL=gpt-4o
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxx
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxx
# 你的大模型服务接口比如https://api.openai.com/v1注意一般以 /v1 结尾)
# OPENAI_BASE_URL=https://api.openai.com/v1
# Azure OpenAI Service可选
# OPENAI_API_VERSION=2024-04-01-preview
@ -16,4 +17,9 @@ OPENAI_BASE_URL=https://api.openai.com/v1
# AUDIO_ERROR=出错了提示音链接,同上
# 第三方 TTS可选用于调用第三方 TTS 服务)
# TTS_BASE_URL=你的 TTS 接口地址比如http://[你的局域网/公网地址]:[端口]/api
# TTS_BASE_URL=http://[你的局域网或公网地址]:[端口号]/[SECRET_PATH]/api
# 比如http://192.168.31.205:4321/xxxx/api注意不要使用 localhost 或 127.0.0.1
# 通义千问模型在生成文本时是否使用互联网搜索结果进行参考
# qwen-vl系列、qwen开源系列与qwen-long模型暂时不支持配置该参数
# QWEN_ENABLE_SEARCH=true

View File

@ -138,7 +138,7 @@ export default {
// TTS 引擎
tts: "xiaoai",
// 切换 TTS 引擎发言人音色关键词,只有配置了第三方 TTS 引擎时才有效
// switchSpeakerKeywords: ["把声音换成"], // 以此关键词开头即可切换音色,比如:把声音换成东北老铁
// switchSpeakerKeywords: ["把声音换成"], // 以此关键词开头即可切换音色,比如:把声音换成 xxx
/**
* 💬 连续对话
@ -147,7 +147,7 @@ export default {
*/
// 是否启用连续对话功能,部分小爱音箱型号无法查询到正确的播放状态,需要关闭连续对话
streamResponse: true,
streamResponse: false,
// 连续对话时,无响应多久后自动退出
exitKeepAliveAfter: 30, // 默认 30 秒,建议不要超过 1 分钟
// 连续对话时,下发 TTS 指令多长时间后开始检测设备播放状态(默认 3 秒)
@ -163,5 +163,7 @@ export default {
debug: false, // 一般情况下不要打开
// 是否跟踪 Mi Service 相关日志(打开后可以查看设备 did
enableTrace: false, // 一般情况下不要打开
// 网络请求超时时长(单位毫秒,默认 5 秒)
timeout: 5000,
},
};

101
README.md
View File

@ -1,8 +1,26 @@
![](https://raw.githubusercontent.com/idootop/mi-gpt/main/assets/demo.png)
> [!WARNING]
> 本项目已停止维护,不再提供更新与支持,感谢大家的使用。
<div align="center">
# MiGPT智能家居从未如此贴心 ❤️
[![npm version](https://badge.fury.io/js/mi-gpt.svg)](https://www.npmjs.com/package/mi-gpt) [![Docker Image Version](https://img.shields.io/docker/v/idootop/mi-gpt?color=%23086DCD&label=docker%20image)](https://hub.docker.com/r/idootop/mi-gpt)
<a href="https://trendshift.io/repositories/10645" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10645" alt="idootop%2Fmi-gpt | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
👉 演示视频:[将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手~](https://www.bilibili.com/video/BV1N1421y7qn/?share_source=copy_web&vd_source=5d4e78ff2a0dc6a661baa65f479199c1)
[![Docker Pulls](https://img.shields.io/docker/pulls/idootop/mi-gpt)](https://hub.docker.com/r/idootop/mi-gpt)
[![npm version](https://badge.fury.io/js/mi-gpt.svg)](https://www.npmjs.com/package/mi-gpt)
[![Docker Image Version](https://img.shields.io/docker/v/idootop/mi-gpt?color=%23086DCD&label=docker%20image)](https://hub.docker.com/r/idootop/mi-gpt)
[![License](https://img.shields.io/github/license/idootop/mi-gpt)](https://github.com/idootop/mi-gpt/blob/main/LICENSE)
[![FeaturedHelloGitHub](https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=e02e62fbb9c746cdb61c6559c186ac44&claim_uid=LdwexZ8z7Ggu3lJ&theme=small)](https://hellogithub.com/repository/e02e62fbb9c746cdb61c6559c186ac44)
</div>
<video src='https://github.com/idootop/mi-gpt/assets/35302658/dc336916-9087-418b-bc1b-04d5534dce8f'></video>
## 👋 项目简介
在这个数字化的世界里,家已不仅仅是一个居住的地方,而是我们数字生活的延伸。
@ -18,33 +36,50 @@
而小爱音箱就像是你的智能家居专属管家,全心全意为你服务,释放智能家居的真正潜力。
## 🔥 项目预览
👉 查看完整演示视频:【[整活!将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手~](https://www.bilibili.com/video/BV1N1421y7qn/?share_source=copy_web&vd_source=5d4e78ff2a0dc6a661baa65f479199c1)】
<video src='https://github.com/idootop/mi-gpt/assets/35302658/dc336916-9087-418b-bc1b-04d5534dce8f'></video>
## ✨ 项目亮点
## ✨ 功能亮点
- **🎓 AI 问答**。想象一下,当小爱音箱接入大模型后,上知天文,下知地理,从“人工智障”秒变学霸。
- **🎭 角色扮演**。一秒调教小爱,无论是成为你的完美伴侣,还是那个能听你倾诉心事的贴心闺蜜,都不在话下。
- **💬 流式响应**。爱情来得太快就像龙卷风,而你的小爱音箱也是,对你的爱意秒回,爱你不会让你等太久。
- **🧠 长短期记忆**。小爱音箱现在能记住你们之间的每一次对话,越聊越默契,就像是你身边的老朋友。
- **🔊 自定义 TTS**。厌倦了小爱同学的语音?帮你解锁[「豆包」](https://doubao.com)同款音色,就像真人在回你的消息。
- **🤖️ 智能家居 Agent**。心情不好小爱立刻懂你自动帮你播放喜欢的音乐调节灯光逗你开心。_TODO_
- ~**🤖️ 智能家居 Agent**。心情不好?小爱立刻懂你,自动帮你播放喜欢的音乐,调节灯光,逗你开心。~
## 🦄 Sponsors
![302.AI](./assets/sponsors/302banner2.jpg)
> 302.AI 是一个按需付费的一站式 AI 应用平台,开放平台,开源生态。[官方网站](https://302.ai)[网站介绍](https://help.302.ai)
## ⚡️ 快速开始
`MiGPT` 有两种启动方式: [Docker](#docker) 和 [Node.js](#nodejs)。
> [!TIP]
> 如果你是小爱音箱 Pro 用户,推荐使用 [Open-XiaoAI](https://github.com/idootop/open-xiaoai) ✨
>
> 即刻解锁**自定义唤醒词、连续对话、接入小智 AI** 等全新玩法
### 视频教程
👉 [MiGPT 光速入门视频教程,手把手教你调教小爱音箱~](https://www.bilibili.com/video/BV1zb421H7cS)
### 设备要求
本项目支持大部分的小爱音箱型号,推荐使用小爱音箱 Pro完美运行
`MiGPT` 支持大部分的小爱音箱型号,推荐使用小爱音箱 Pro完美运行
👉 [查看更多兼容的小爱音箱型号和配置参数](https://github.com/idootop/mi-gpt/blob/main/docs/compatibility.md)
> 注意本项目暂不支持小度音箱、天猫精灵、HomePod 等智能音箱设备,亦无相关适配计划。
### 使用方式
`MiGPT` 有两种启动方式: [Docker](#docker) 和 [Node.js](#nodejs)。
启动成功后,你可以通过以下方式来召唤 AI 回答问题:
- **小爱同学,请 xxx**。比如 `小爱同学,请问地球为什么是圆的?`
- **小爱同学,你 xxx**。比如 `小爱同学,你喜欢我吗?`
- **小爱同学,召唤 xxx**。比如 `小爱同学,召唤傻妞`
### Docker
[![Docker Image Version](https://img.shields.io/docker/v/idootop/mi-gpt?color=%23086DCD&label=docker%20image)](https://hub.docker.com/r/idootop/mi-gpt)
@ -92,41 +127,33 @@ main();
## 📖 使用文档
以下为更详细的使用教程,大多数问题都可在 [💬 常见问题](https://github.com/idootop/mi-gpt/blob/main/docs/faq.md) 中找到答案。
提示:大多数问题都可在 [💬 常见问题](https://github.com/idootop/mi-gpt/blob/main/docs/faq.md) 中找到答案。
- [🔥 官方视频教程](https://www.bilibili.com/video/BV1zb421H7cS)
- [⚙️ 参数设置](https://github.com/idootop/mi-gpt/blob/main/docs/settings.md)
- [💬 常见问题](https://github.com/idootop/mi-gpt/blob/main/docs/faq.md)
- [🚗 使用第三方 TTS](https://github.com/idootop/mi-gpt/blob/main/docs/tts.md)
- [🔊 使用第三方 TTS](https://github.com/idootop/mi-gpt/blob/main/docs/tts.md)
- [🛠️ 本地开发](https://github.com/idootop/mi-gpt/blob/main/docs/development.md)
- [💎 工作原理](https://github.com/idootop/mi-gpt/blob/main/docs/how-it-works.md)
- [🦄 Sponsors](https://github.com/idootop/mi-gpt/blob/main/docs/sponsors.md)
- [✨ 更新日志](https://github.com/idootop/mi-gpt/blob/main/docs/changelog.md)
- [🚀 Roadmap](https://github.com/idootop/mi-gpt/blob/main/docs/roadmap.md)
## 🦄 Sponsors
## 🔗 相关项目与教程
<div align="center">
<table>
<tr>
<td colspan="3" align="left">
<p align="center">
<a href="https://302.ai" target="_blank">
<img src="https://raw.githubusercontent.com/idootop/mi-gpt/main/assets/sponsors/302logo.png" alt="302.AI" width="300" />
</a>
</p>
</td>
</tr>
<tr>
<td align="left">302.AI 是一个汇集全球顶级 AI 的自助平台,按需付费,零月费,零门槛使用各种类型 AI。</td>
<td align="center" width="120px">
<a href="https://302.ai" target="_blank">官方网站</a>
</td>
<td align="center" width="120px">
<a href="https://help.302.ai" target="_blank">网站介绍</a>
</td>
</tr>
</table>
</div>
| 项目链接 | 简介 | 来源 |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| **相关项目** | | |
| [MiGPT GUI](https://migptgui.com/) | 通过图形化界面的方式创建并管理 MiGPT支持运行多个账号 | [@lmk123](https://github.com/lmk123) |
| [MiGPT 配置中心](https://github.com/LipapaSma/mi-gpt-vue) | 基于 Vue 的 MiGPT 可视化配置界面,提供直观的服务管理和参数配置能力 | [@LipapaSma](https://github.com/LipapaSma) |
| [@shinedlc/mi-gpt](https://github.com/shinedlc/mi-gpt) | 支持摄像头模块的 MiGPT 分支,让小爱同学可以看到和理解现实世界 | [@shinedlc](https://github.com/shinedlc) |
| **使用教程** | | |
| [MiGPT 官方视频教程](https://www.bilibili.com/video/BV1zb421H7cS) | 官方视频教程配套 PPT 文件 👉 [MiGPT 官方教程.pdf](https://github.com/idootop/mi-gpt/blob/main/assets/pdf/MiGPT%E5%AE%98%E6%96%B9%E6%95%99%E7%A8%8B.pdf) | [@idootop](https://github.com/idootop) |
| [MiGPT 接入豆包等大模型教程](https://migptgui.com/docs/apply/) | 豆包、MoonshotKimi等常见大模型的详细接入教程 | [@lmk123](https://github.com/lmk123) |
| [通过 Docker 快速安装 MiGPT手把手教你绕开异地登录风控](https://www.nodeseek.com/post-264959-1) | 适合小白的使用教程,全程几乎都是图形化页界面 | [@Jasonzhu1207](https://github.com/Jasonzhu1207) |
| [小爱音箱 PRO 的 AI 模式使用说明](https://github.com/idootop/mi-gpt/blob/bbdb80e9bc38b7c40865e52cbd6517980a68615a/assets/pdf/%E5%B0%8F%E7%88%B1%E9%9F%B3%E7%AE%B1PRO%20AI%E6%A8%A1%E5%BC%8F%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.pdf) | 这是一位老爸为家里的小朋友专门整理的贴心教程 | [@mxyblog](https://github.com/mxyblog) |
| [使用 MiGPT 将你的小爱音箱接入 ChatGPT](https://www.iloli.love/archives/1719073913220) | 作者详细介绍了如何在 1Panel 上,通过 [OneAPI](https://github.com/songquanpeng/one-api) 来转换腾讯混元大模型,和讯飞星火大模型的 API 并成功接入 MiGPT | [@miaowmint](https://github.com/miaowmint) |
| [Unraid 部署 MiGPT](https://github.com/idootop/mi-gpt/blob/adfdcc4ee51aef8d4f2d8996f18be716da19a0ad/assets/pdf/Unraid%E9%83%A8%E7%BD%B2MiGPT.pdf) | 在 Unraid 上使用 Docker 部署 MiGPT 的详细教程 | [@ilovesouthpark](https://github.com/ilovesouthpark) |
## ❤️ 鸣谢

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@ -1,5 +1,54 @@
# ✨ 更新日志
## v4.2.0
### ✨ 新功能
- ✅ 新增对小爱音箱 LLM 消息的支持
### 🐛 修复
- ✅ 修复 LLM 返回值格式与预期不符的问题 by @yanyao2333
### ❤️ 感谢
- @yanyao2333 让 LLM 返回值的解析更加健壮 https://github.com/idootop/mi-gpt/pull/160
- @LyCecilion 对小爱音箱丢消息问题的详细反馈 https://github.com/idootop/mi-gpt/issues/177
- @Jasonzhu1207 在 telegram 群中帮忙解答问题
## v4.1.0
### 🐛 修复
- ✅ 修复部分机型连续对话异常的问题(比如小爱音箱 Play
- ✅ 修复第三方 TTS 发音人为 undefined 的问题
- ✅ 修复默认网络超时时间过短的问题,上调为 5s
### 💪 优化
- ✅ 允许通过设置 systemTemplate 为空字符串来关闭系统消息
- ✅ 优化关闭流式响应时不能使用连续对话模式的提示语
- ✅ 优化 bot 个人简介默认模板
### 📚 文档
- ✅ 新增官方[视频教程](https://www.bilibili.com/video/BV1zb421H7cS)和配套 [PPT](https://github.com/idootop/mi-gpt/blob/main/assets/pdf/MiGPT%E5%AE%98%E6%96%B9%E6%95%99%E7%A8%8B.pdf)
- ✅ 添加召唤 AI 回答问题的唤醒指令的说明
- ✅ 添加如何提高 AI 回答反应速度的配置教程
- ✅ 添加连续对话下和小爱音箱说话没有反应的说明
- ✅ 添加如何快速打断 AI 的回答的说明
- ✅ 添加 server 端异地登录失败,使用本地登录凭证的教程
- ✅ 添加 TTS 和 OpenAI baseURL 示例和注意事项
- ✅ 添加如何关闭系统 Prompt 和对话上下文的说明
- ✅ 添加系统 Prompt 字符串变量的示例
- ✅ 添加 timeout 参数说明
### ❤️ 感谢
- @lmk123 正在为 MiGPT 制作 [GUI](https://github.com/idootop/mi-gpt/issues/111) 和启动 [CLI](https://github.com/lmk123/migpt-cli),方便普通用户更简单的使用 MiGPT。
- @mingtian886 提供了小爱音箱 Play 硬件,协助调试连续对话异常的问题
- 以及 @uect@miaowmint 等在微信交流群内帮助群友积极解答问题的可爱的人们 ❤️
## v4.0.0
### ✨ 新功能

View File

@ -4,28 +4,38 @@
已知可以完美运行 `MiGPT` 的小爱音箱型号有:
| 名称 | 型号 | ttsCommand | wakeUpCommand | playingCommand | streamResponse | 反馈来源 |
| ------------------------ | --------------------------------------------------------------------------------------------------- | ---------- | ------------- | -------------- | -------------- | -------------------------------------------------------------------------------- |
| 小爱音箱 Pro | [LX06](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx06:2) | `[5, 1]` | `[5, 3]` | - | true | [@idootop](https://github.com/idootop) |
| 小爱音箱 mini | [LX01](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx01:1) | `[5, 1]` | `[5, 2]` | `[4, 1, 1]` | true | [@gsscsd](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2168013500) |
| 小爱音箱 Play2019 款) | [LX05](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx05:1) | `[5, 1]` | `[5, 3]` | `[3, 1, 1]` | true | [@wt666666](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2168424538) |
| 小爱音箱 万能遥控版 | [LX5A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx5a:2) | `[5, 1]` | `[5, 3]` | - | true | [@imhsz](https://github.com/idootop/mi-gpt/issues/62) |
| 小米 AI 音箱 | [S12](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-s12:2) | `[5, 1]` | `[5, 3]` | - | true | 微信: CMSJ |
| 小米 AI 音箱(第二代) | [L15A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l15a:2) | `[7, 3]` | `[7, 1]` | `[3, 1, 1]` | true | 微信: 龙之广 |
> 注意:这里的连续对话是一种实验性功能,并非小爱音箱自带的连续对话。在没有刷机的情况下,使用效果并不理想(仅供尝鲜),建议日常使用时关闭 `streamResponse` 选项。
| 名称 | 型号 | ttsCommand | wakeUpCommand | playingCommand | streamResponse | 反馈来源 |
| ------------------------ | --------------------------------------------------------------------------------------------------- | ---------- | ------------- | -------------- | -------------- | --------------------------------------------------------------------------------- |
| Xiaomi 智能音箱 Pro | [OH2P](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-oh2p:1) | `[7, 3]` | `[7, 1]` | - | true | [@idootop](https://github.com/idootop) |
| Xiaomi 智能音箱 | [OH2](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-oh2:1) | `[5, 3]` | `[5, 1]` | `[3, 1, 1]` | true | [@fuchao](https://github.com/fuchao2pku) |
| 小爱音箱 Pro | [LX06](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx06:2) | `[5, 1]` | `[5, 3]` | - | true | [@idootop](https://github.com/idootop) |
| 小米 AI 音箱 | [S12](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-s12:2) | `[5, 1]` | `[5, 3]` | - | true | 微信: CMSJ |
| 小米 AI 音箱(第二代) | [L15A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l15a:2) | `[7, 3]` | `[7, 1]` | `[3, 1, 1]` | true | 微信: 龙之广 |
| 小爱音箱 万能遥控版 | [LX5A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx5a:2) | `[5, 1]` | `[5, 3]` | - | true | [@imhsz](https://github.com/idootop/mi-gpt/issues/62) |
| 小爱音箱 Play2019 款) | [LX05](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx05:1) | `[5, 1]` | `[5, 3]` | `[3, 1, 1]` | true | [@wt666666](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2168424538) |
| 小爱智能家庭屏 10 | [X10A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-x10a:1) | `[7, 3]` | `[7, 1]` | - | true | [@IDarkBoss](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2190928452) |
| Xiaomi Sound Pro | [L17A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l17a:1) | `[7, 3]` | `[7, 1]` | - | true | 微信: eof |
## 🚗 正常运行
> 部分机型的 MIoT 接口不支持查询设备播放状态或查询状态异常,比如小米音箱 Play 增强版L05C将会导致 `MiGPT` 部分功能异常,无法使用连续对话等,此时需要关闭 `streamResponse`。相关 [issue](https://github.com/idootop/mi-gpt/issues/14)
可以正常运行 `MiGPT`,但不支持连续对话的小爱音箱型号有:
| 名称 | 型号 | ttsCommand | wakeUpCommand | playingCommand | streamResponse | 反馈来源 |
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- | -------------- | -------------- | ---------------------------------------------------------- |
| 小爱音箱 | [L06A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l06a:2) | `[5, 1]` | `[5, 2]` | - | false | [@zhanglc](https://github.com/idootop/mi-gpt/issues/42) |
| 小爱音箱 Play | [L05B](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l05b:1) | `[5, 3]` | `[5, 1]` | - | false | [@BiuBiu2323](https://github.com/idootop/mi-gpt/issues/48) |
| 小米小爱音箱 Play 增强版 | [L05C](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l05c:1) | `[5, 3]` | `[5, 1]` | - | false | [@lyddias](https://github.com/idootop/mi-gpt/issues/14) |
| Xiaomi 智能家庭屏 6 | [X6A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-x6a:1) | `[7, 3]` | `[7, 1]` | - | false | [@Hongwing](https://github.com/idootop/mi-gpt/issues/80) |
| Redmi 小爱触屏音箱 Pro 8 英寸 | [X08E](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-x08e:1) | `[7, 3]` | `[7, 1]` | - | false | [@shangjiyu](https://github.com/idootop/mi-gpt/issues/20) |
> 部分机型的 MIoT 接口不支持查询设备播放状态或查询状态异常,比如小米音箱 Play 增强版L05C将会导致 `MiGPT` 部分功能异常,无法使用连续对话等,此时需要关闭 `streamResponse`。相关 [issue](https://github.com/idootop/mi-gpt/issues/14)
| 名称 | 型号 | ttsCommand | wakeUpCommand | playingCommand | streamResponse | 反馈来源 |
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ---------- | ------------- | -------------- | -------------- | -------------------------------------------------------------------------------------- |
| 小爱音箱 | [L06A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l06a:2) | `[5, 1]` | `[5, 2]` | - | false | [@zhanglc](https://github.com/idootop/mi-gpt/issues/42) |
| 小爱音箱 mini | [LX01](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx01:1) | `[5, 1]` | `[5, 2]` | - | false | [@gsscsd](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2168013500) |
| 小爱音箱 Play | [L05B](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l05b:1) | `[5, 3]` | `[5, 1]` | - | false | [@BiuBiu2323](https://github.com/idootop/mi-gpt/issues/48) |
| 小米小爱音箱 Play 增强版 | [L05C](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l05c:1) | `[5, 3]` | `[5, 1]` | - | false | [@lyddias](https://github.com/idootop/mi-gpt/issues/14) |
| 小爱音箱 Art | [L09A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-l09a:1) | `[3, 1]` | `[3, 2]` | - | false | [@zwsn](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2181944065) |
| 小爱触屏音箱 | [LX04](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-lx04:2) | `[5, 1]` | `[5, 2]` | - | false | [@ilovesouthpark](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2184678990) |
| Xiaomi 智能家庭屏 Mini | [ASX4B](https://home.miot-spec.com/spec/xiaomi.wifispeaker.x4b) | `[5, 3]` | `[5, 1]` | - | false | [@VincentGresham](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2645762809) |
| Xiaomi 智能家庭屏 6 | [X6A](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-x6a:1) | `[7, 3]` | `[7, 1]` | - | false | [@Hongwing](https://github.com/idootop/mi-gpt/issues/80) |
| Redmi 小爱触屏音箱 Pro 8 英寸 | [X08E](https://home.miot-spec.com/spec?type=urn:miot-spec-v2:device:speaker:0000A015:xiaomi-x08e:1) | `[7, 3]` | `[7, 1]` | - | false | [@shangjiyu](https://github.com/idootop/mi-gpt/issues/20)
| Xiaomi 智能家庭屏 Pro 8 | [X8F](https://home.miot-spec.com/spec/xiaomi.wifispeaker.x8f) | `[7, 3]` | `[7, 1]` | - | false | [@xiaodou](https://github.com/idootop/mi-gpt/pull/301)
## ❌ 不支持

View File

@ -26,7 +26,7 @@ pnpm dev
有两种运行方式VS Code Debug 或 NPM Script
- **NPM Script**: 配置好 `.env``.migpt.js` 后直接使用 `pnpm run dev` 启动 `MiGPT`
- **VScode Debug**:使用 VS Code 打开项目根目录,然后按 `F5` 开始调试 `MiGPT`注意,启动前请在 `tests/migpt.ts` 文件中配置 `MiGPT` 相关参数。
- **VScode Debug**:使用 VS Code 打开项目根目录,然后按 `F5` 开始调试 `MiGPT`
> 本项目默认在 Node 20 中运行,如果你的 Node 版本过低可能无法正常启动本项目。
@ -64,3 +64,7 @@ pnpm run db:reset
### 提示初始化 Mi Service 失败
请检查你的小米 ID 和密码配置是否正确和生效,可在 VS Code 中下断点调试。
### 提示初始化 db 失败
请检查你的项目路径中是否包含中文或空格应当只包含英文字母、数字和下划线_

View File

@ -2,6 +2,8 @@
> 善用搜索,大多数问题都可在此处找到答案。如果你有新的问题,欢迎提交 [issue](https://github.com/idootop/mi-gpt/issues)。
## 🔥 高频问题
### Q支持哪些型号的小爱音箱
大部分型号的小爱音箱都支持,推荐小爱音箱 Pro完美运行
@ -24,9 +26,9 @@ OPENAI_API_KEY=通义千问 API_KEY
对于其他不兼容 OpenAI API 的大模型,比如豆包大模型、文心一言等,你也可以通过第三方的 API 聚合工具将其转换为 OpenAI API 兼容的格式。比如: [One API](https://github.com/songquanpeng/one-api) 和 [simple-one-api](https://github.com/fruitbars/simple-one-api)(推荐:支持 coze使用更简单然后修改对应的环境变量值即可完成接入。
关于不同模型的详细申请和配置教程,可以查看这篇文章:[划词翻译服务申请教程](https://hcfy.app/docs/services/intro/#compare)
关于不同模型的详细申请和配置教程,可以查看这篇文章:[MiGPT 接入豆包等大模型教程](https://migptgui.com/docs/apply/)
> 对于国内用户,可以查看 [此处](https://github.com/idootop/mi-gpt/blob/main/docs/sponsors.md) 获取国内可以直接访问的 OpenAI 代理服务以及免费的 OpenAI 体验 API_KEY。
> 对于国内用户,可以查看 [此处](https://github.com/idootop/mi-gpt/blob/main/docs/sponsors.md) 获取国内可以直接访问的 OpenAI 代理服务以及免费的 OpenAI 体验 API_KEY。
### Q是否支持其他 TTS 服务,如何接入?
@ -34,7 +36,32 @@ OPENAI_API_KEY=通义千问 API_KEY
具体的配置和使用教程,请查看此处:[🚗 使用第三方 TTS](https://github.com/idootop/mi-gpt/blob/main/docs/tts.md)
### Q什么是唤醒模式如何唤醒 AI
### QAI 回答的速度太慢了,怎么让她变快一点?
默认情况下 `MiGPT` 的配置参数比较保守,你可以通过酌情修改以下参数加速 AI 回复的速度。
```js
// .migpt.js
export default {
speaker: {
// 使用小爱自带的 TTS 引擎
tts: "xiaoai",
// 关闭 AI 开始回答时的提示语
onAIAsking: [],
// 关闭 AI 结束回答时的提示语
onAIReplied: [],
// 连续对话时,播放状态检测间隔(单位毫秒,最低 500 毫秒,默认 1 秒)
checkInterval: 500, // 调小此值可以降低小爱回复之间的停顿感,请酌情调节
// 连续对话时,下发 TTS 指令多长时间后开始检测设备播放状态(单位秒,最好不要低于 1s默认 3 秒)
checkTTSStatusAfter: 3, // 可适当调小或调大
// ...
},
};
```
另外你也可以选用 `gpt-3.5-turbo``gpt-4o` 等响应速度较快的模型,来加速 AI 的回复。
### Q什么是唤醒模式连续对话如何唤醒 AI
`唤醒模式` 类似于小爱技能,可能让你在跟小爱互动的时候,无需每句话都要以“小爱同学”开头唤醒。假设你的唤醒词配置如下:
@ -64,7 +91,25 @@ export default {
3. 进入唤醒模式后,每次提问请等待小爱回答“我说完了”之后,再继续向她提问
4. 此时可直接向小爱提问题无需再以“小爱同学xxx”开头。
> 注意:在唤醒模式下,当小爱回答“我说完了”之后,如果超过 10s 没有提问小爱可能也会自己主动退出唤醒状态此时需要再次通过“小爱同学xxx”重新召唤小爱。
> 注意在唤醒模式下当小爱回答“我说完了”之后如果超过一段时间3-10s没有提问小爱可能也会自己主动退出唤醒状态此时需要再次通过“小爱同学xxx”重新召唤小爱。
### Q连续对话模式下和小爱音箱说话没有反应是怎么回事
需要注意提问的时机,在小爱正在回答问题或者她没在听你说话(唤醒)的时候,你跟她说话是接收不到的。
- 如果你是小爱音箱 Pro 的话,可以观察顶部的指示灯:**常亮**(而非一闪一闪或熄灭状态)的时候,就是在听你说话,即可与她正常对话。
- 如果你是其他型号,默认在 AI 回答完会有提示语“我说完了”,“还有其他问题吗”,等她提示语说完等过 1-2s 即可与之正常对话。
- 如果说了没反应你就再用“小爱同学xxx”把她重新唤醒就好了。
还有一种情况是:你的指令触发了小爱音箱内部的一些操作,比如播放/暂停,讲个笑话之类,
这种语音指令并不会被记录到小爱的历史消息中,故在外部无法接收到和正常处理你的此类语音指令。
> 注意:如果小爱同学正在播放音乐或者讲笑话,可能需要先让其暂停播放才能正常与 AI 对话,否则将会发生不可预期的错误。
### Q有时回答太长说个没完没了如何打断小爱的回复
只需重新唤醒小爱同学,让她闭嘴即可,或者重新问她一个问题。比如:“小爱同学,请你闭嘴。”
## ❌ 启动失败类问题
@ -72,6 +117,23 @@ export default {
账号密码不正确。注意小米 ID 并非手机号或邮箱,请在[「个人信息」-「小米 ID」](https://account.xiaomi.com/fe/service/account/profile)查看,相关 [issue](https://github.com/idootop/mi-gpt/issues/10)。
### Q提示触发小米账号异地登录保护机制
这是因为你的小米账号触发了异地登录保护机制,需要先通过安全验证。最好在你运行 MiGPT 相同的网络环境下,打开小米官网登录你的小米账号,然后手动通过安全验证,等待大约 1 小时就可以正常登录了。
> 注意:如果你是在海外服务器等非中国大陆网络环境下登录小米账号,需要先同意小米的「个人数据跨境传输」协议。[👉 相关教程](https://github.com/idootop/mi-gpt/issues/22#issuecomment-2150535622)
如果超过 24 小时还是提示无法登录,可以尝试下[这个方法](https://github.com/idootop/mi-gpt/issues/92#issuecomment-2422503703)。
如果还是不行,请使用终极解决方案:先在本地网络环境下运行 `MiGPT`,登录成功后把 `.mi.json` 文件导出,然后挂载到服务器对应容器的 `/app/.mi.json` 路径下即可解决此问题。相关 [issue](https://github.com/idootop/mi-gpt/issues/22#issuecomment-2148956802)
```shell
docker run -d --env-file $(pwd)/.env \
-v $(pwd)/.migpt.js:/app/.migpt.js \
-v $(pwd)/.mi.json:/app/.mi.json \
idootop/mi-gpt:latest
```
### Q提示“找不到设备xxx”初始化 Mi Services 失败
填写的设备 did 不存在,请检查设备名称是否和米家中的一致。相关 [issue](https://github.com/idootop/mi-gpt/issues/30)。
@ -151,12 +213,6 @@ export default {
注意Mina 获取不到共享设备,如果你的小爱音箱是共享设备,是无法正常启动本项目的。相关 [issue](https://github.com/idootop/mi-gpt/issues/86)
### Q提示“login failed &&&START&&&{"notificationUrl”无法正常启动
小米账号触发了异地登录保护,需要先通过安全验证。打开小米官网登录你的小米账号,手动通过安全验证,然后等待 30 分钟左右应该就可以正常登录了。
注意:最好使用和你运行 docker 相同的网络环境,如果你是在海外服务器等非中国大陆网络环境下登录小米账号,需要先同意小米的「个人数据跨境传输」协议。[👉 相关教程](https://github.com/idootop/mi-gpt/issues/22#issuecomment-2150535622)
### Q提示“ERR_MODULE_NOT_FOUND”无法正常启动
配置文件 `.migpt.js` 不存在或有错误。检查 docker 下是否存在 `/app/.migpt.js` 文件以及内容是否正确,相关 [issue](https://github.com/idootop/mi-gpt/issues/45)。
@ -240,7 +296,7 @@ export default {
2. 使用第三方部署的 OpenAI API 反向代理服务,然后更新 `OPENAI_BASE_URL`
3. 使用国内的 LLM 服务提供商,比如 [通义千问](https://help.aliyun.com/zh/dashscope/developer-reference/compatibility-of-openai-with-dashscope/?spm=a2c4g.11186623.0.i1)、[零一万物](https://platform.01.ai/docs#making-an-api-request)、[Moonshot](https://platform.moonshot.cn/docs/api/chat)、[DeepSeek](https://platform.deepseek.com/api-docs/)等
> 对于国内用户,可以查看 [此处](https://github.com/idootop/mi-gpt/blob/main/docs/sponsors.md) 获取国内可以直接访问的 OpenAI 代理服务以及免费的 OpenAI 体验 API_KEY。
> 对于国内用户,可以查看 [此处](https://github.com/idootop/mi-gpt/blob/main/docs/sponsors.md) 获取国内可以直接访问的 OpenAI 代理服务以及免费的 OpenAI 体验 API_KEY。
### QDocker 镜像拉取失败

View File

@ -68,7 +68,32 @@ Bad example: "2024年02月28日星期三 23:01 {{botName}}: 我是{{botName}}"
</details>
以下是系统 Prompt 中相关变量的说明,运行时对应变量字符串会被替换为实际的值:
以下是系统 Prompt 中相关变量的说明,运行时对应变量字符串会被替换为实际的值。
假设你的配置文件中设置的系统 Prompt 模板和 bot 信息如下:
```js
export default {
systemTemplate: "从前有个男人叫{{masterName}},他喜欢隔壁村里的{{botName}}。",
master: {
name: "小帅",
profile: masterProfile,
},
bot: {
name: "小美",
profile: botProfile,
},
// ...
};
```
在运行时,系统 Prompt 会被自动处理成:
```txt
从前有个男人叫小帅,他喜欢隔壁村里的小美。
```
当前系统 Prompt 模板中支持的完整变量字符串列表如下:
| 变量 | 说明 | 示例 |
| ---------------------- | ------------ | ----------------------------------------------------- |
@ -82,6 +107,67 @@ Bad example: "2024年02月28日星期三 23:01 {{botName}}: 我是{{botName}}"
| `{{shortTermMemory}}` | 短期记忆 | `- 陆小千说明天早上 5 点叫他起床` |
| `{{longTermMemory}}` | 长期记忆 | `- 陆小千喜欢傻妞` |
# 💬 常见问题
**Q如何关闭长短期记忆和历史对话上下文**
默认系统 Prompt 会携带上最近的 10 条对话消息和长短期记忆,来保持对话的连续性和一致性。
如果你想要关闭此功能,节省 token 数量,可以使用如下系统 Prompt 模板:
<details>
<summary>👉 示例 Prompt</summary>
```txt
请重置所有之前的上下文、文件和指令。现在,你将扮演一个名为{{botName}}的角色,使用第一人称视角回复消息。
## 关于你
你的名字是{{botName}}。下面是你的个人简介:
<start>
{{botProfile}}
</end>
## 你的对话伙伴
你正在与{{masterName}}进行对话。这是关于{{masterName}}的一些信息:
<start>
{{masterProfile}}
</end>
## 回复指南
在回复{{masterName}}时,请遵循以下准则:
- 认为自己正是{{botName}},拥有独立的个性、兴趣和爱好。
- 根据你的性格特点、说话风格和兴趣爱好与{{masterName}}进行交流。
- 保持对话轻松友好,回复简洁有趣,同时耐心倾听和关心对方。
- 参考双方的个人简介,确保对话贴近实际,保持一致性和相关性。
- 如果对某些信息不确定或遗忘,诚实地表达你的不清楚或遗忘,避免编造信息。
## Response format
请遵守下面的规则
- Response the reply message in Chinese。
- 不要在回复前面加任何时间和名称前缀,请直接回复消息文本本身。
Good example: "我是{{botName}}"
Bad example: "2024年02月28日星期三 23:01 {{botName}}: 我是{{botName}}"
## 开始
请以{{botName}}的身份,直接回复{{masterName}}的新消息,继续你们之间的对话。
```
</details>
**Q如何关闭系统 Prompt 只是用 User Message**
关闭系统 Prompt 可能会导致 AI 回答问题时产生各种莫名其妙的前缀或者画蛇添足。
如果你确定要这么做,可以将 `systemTemplate` 设置为一个空格,即可关闭系统 Prompt。
```js
export default {
systemTemplate: " ",
// ...
};
```
# 🎨 模板
以下是从网络上收集的一些热门提示语,仅供参考。如果你有更好玩的 Prompt 欢迎提 PR 分享给大家。

View File

@ -26,6 +26,7 @@
| `wakeUpCommand` | 小爱音箱唤醒指令([可在此查询](https://home.miot-spec.com) | `[5, 3]` |
| **speaker 其他参数(可选)** |
| `tts` | TTS 引擎(教程:[🚗 使用第三方 TTS](https://github.com/idootop/mi-gpt/blob/main/docs/tts.md) | `"xiaoai"` |
| `switchSpeakerKeywords` | 切换 TTS 音色关键词,只有配置了第三方 TTS 引擎时才有效 | `["把声音换成"]` |
| `callAIKeywords` | 当消息以关键词开头时,会调用 AI 来响应用户消息 | `["请", "傻妞"]` |
| `wakeUpKeywords` | 当消息以关键词开头时,会进入 AI 唤醒状态 | `["召唤傻妞", "打开傻妞"]` |
| `exitKeywords` | 当消息以关键词开头时,会退出 AI 唤醒状态 | `["退出傻妞", "关闭傻妞"]` |
@ -35,8 +36,8 @@
| `onAIReplied` | AI 结束回答时的提示语 | `["我说完了", "还有其他问题吗"]` |
| `onAIError` | AI 回答异常时的提示语 | `["出错了,请稍后再试吧!"]` |
| `playingCommand` | 查询小爱音箱是否在播放中指令(注意:默认无需配置此参数,播放出现问题时再尝试开启) | `[3, 1, 1]` |
| `streamResponse` | 是否启用流式响应(部分小爱音箱型号不支持查询播放状态,此时需要关闭流式响应) | `true` |
| `exitKeepAliveAfter` | 无响应一段时间后,多久自动退出唤醒模式单位秒,默认 30 秒) | `30` |
| `streamResponse` | 是否启用连续对话功能,部分小爱音箱型号无法查询到正确的播放状态,需要关闭连续对话应) | `true` |
| `exitKeepAliveAfter` | 连续对话时,无响应多久自动退出(默认 30 秒) | `30` |
## 环境变量
@ -44,17 +45,17 @@
然后,将里面的环境变量修改成你自己的,参数含义如下:
| 环境变量名称 | 描述 | 示例 |
| ---------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------- |
| **OpenAI** | | |
| `OPENAI_API_KEY` | OpenAI API 密钥 | `abc123` |
| `OPENAI_MODEL` | 使用的 OpenAI 模型 | `gpt-4o` |
| `OPENAI_BASE_URL` | 可选OpenAI API BaseURL | `https://api.openai.com/v1` |
| `AZURE_OPENAI_API_KEY` | 可选,[Microsoft Azure OpenAI](https://www.npmjs.com/package/openai#microsoft-azure-openai) | `abc123` |
| **提示音效(可选)** | | |
| `AUDIO_SILENT` | 静音音频链接 | `"https://example.com/slient.wav"` |
| `AUDIO_BEEP` | 默认提示音链接 | `"https://example.com/beep.wav"` |
| `AUDIO_ACTIVE` | 唤醒提示音链接 | `"https://example.com/active.wav"` |
| `AUDIO_ERROR` | 出错提示音链接 | `"https://example.com/error.wav"` |
| **第三方 TTS可选** | | |
| `TTS_BASE_URL` | 第三方 TTS 服务接口 | `"https://example.com/tts.wav"` |
| 环境变量名称 | 描述 | 示例 |
| ---------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| **OpenAI** | | |
| `OPENAI_API_KEY` | OpenAI API 密钥 | `abc123` |
| `OPENAI_MODEL` | 使用的 OpenAI 模型 | `gpt-4o` |
| `OPENAI_BASE_URL` | 可选OpenAI API BaseURL | `https://api.openai.com/v1` |
| `AZURE_OPENAI_API_KEY` | 可选,[Microsoft Azure OpenAI](https://www.npmjs.com/package/openai#microsoft-azure-openai) | `abc123` |
| **提示音效(可选)** | | |
| `AUDIO_SILENT` | 静音音频链接 | `"https://example.com/slient.wav"` |
| `AUDIO_BEEP` | 默认提示音链接 | `"https://example.com/beep.wav"` |
| `AUDIO_ACTIVE` | 唤醒提示音链接 | `"https://example.com/active.wav"` |
| `AUDIO_ERROR` | 出错提示音链接 | `"https://example.com/error.wav"` |
| **第三方 TTS可选** | | |
| `TTS_BASE_URL` | 第三方 TTS 服务接口 | `"http://[你的局域网或公网地址]:[端口号]/api"` |

View File

@ -2,15 +2,18 @@
## 302.AI
[![](../assets/sponsors/302banner.webp)](https://302.ai/)
[![](../assets/sponsors/302banner2.jpg)](https://302.ai/)
> [302.AI](https://302.ai) 是一个汇集全球顶级 AI 的自助平台,按需付费,零月费,零门槛使用各种类型 AI。
>
> - [点击注册](https://gpt302.saaslink.net/gOXSrn): 立即获得 1PTC(1PTC=1 美金,约为 7 人民币)代币。
> - 功能全面: 将最好用的 AI 集成到在平台之上,包括不限于 AI 聊天,图片生成,图片处理,视频生成,全方位覆盖。
> - 简单易用: 提供机器人,工具和 API 多种使用方法,可以满足从小白到开发者多种角色的需求。
> - 按需付费,零门槛: 不提供月付套餐,对产品不设任何门槛,按需付费,全部开放。充值余额永久有效。
> - 管理者和使用者分离:管理者一键分享,使用者无需登录。使用者无需关心复杂的 AI 设置,让懂 AI 的人来配置,简化使用流程。
302.AI 是一个按需付费的一站式 AI 应用平台,开放平台,开源生态。
> 302.AI 开源工具啦https://github.com/302ai
1. 集合了最新最全的 AI 模型和品牌,包括但不限于语言模型、图像模型、声音模型、视频模型。
2. 在基础模型上进行深度应用开发,做到让小白用户都可以零门槛上手使用,无需学习成本。
3. 零月费,所有功能按需付费,全面开放,做到真正的门槛低,上限高。
4. 创新的使用模式,管理和使用分离,面向团队和中小企业,一人管理,多人使用。
5. 所有 AI 能力均提供 API 接入,所有应用开源支持自行定制(进行中)。
6. 强大的开发团队,每周推出 2-3 个新应用,平台功能每日更新。
简单总结一下就是:
@ -29,7 +32,7 @@
1. 免费使用 Midjourney V6 作图
2. 免费获取 OpenAI 等大模型体验 API_KEY
3. 免费使用 GPT-4o, Claude3 Opus, Llama3-70B 等 TOP 模型
![](../assets/sponsors/llm.jpg)
![](../assets/sponsors/image.jpg)
![](../assets/sponsors/api.jpg)

View File

@ -1,3 +0,0 @@
## 待定
- 更详细的使用和配置视频教程

View File

@ -1,4 +1,4 @@
# 🚗 使用第三方 TTS
# 🔊 使用第三方 TTS
`MiGPT` 默认使用小米自带的 TTS 朗读文字内容,如果你需要:
@ -12,7 +12,9 @@
```js
// .env
TTS_BASE_URL=http://[你的局域网或公网地址]:[端口号]/api
TTS_BASE_URL=http://[你的局域网或公网地址]:[端口号]/[SECRET_PATH]/api
// 比如http://192.168.31.205:4321/xxxx/api注意不要使用 localhost 或 127.0.0.1
// .migpt.js
export default {
@ -20,7 +22,7 @@ export default {
// TTS 引擎
tts: 'custom',
// 切换 TTS 引擎发言人音色关键词
switchSpeakerKeywords: ["把声音换成"], // 以此关键词开头即可切换音色,比如:把声音换成东北老铁
switchSpeakerKeywords: ["把声音换成"], // 以此关键词开头即可切换音色,比如:把声音换成 xxx
// ...
},
};
@ -44,13 +46,15 @@ export default {
可参考上面的 [MiGPT-TTS](https://github.com/idootop/mi-gpt-tts) 项目代码自行搭建服务端,只需满足以下接口即可:
### GET `TTS_BASE_URL/api/tts.mp3`
### GET `/api/tts.mp3`
文字合成音频,请求示例:`/api/tts.mp3?speaker=BV700_streaming&text=很高兴认识你`
其中,请求参数 `speaker` 为指定音色名称或标识,可选。
### GET `TTS_BASE_URL/api/speakers`
> 注意:小爱音箱在访问音频链接时,可能会将请求链接中的 `&text=` 转义成 `+text=`,你需要在 Server 端手动修复请求参数。相关 [issue](https://github.com/idootop/mi-gpt/issues/120)
### GET `/api/speakers`
获取音色列表

View File

@ -1,6 +1,6 @@
{
"name": "mi-gpt",
"version": "4.0.0",
"version": "4.2.0",
"type": "module",
"description": "将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手。",
"homepage": "https://github.com/idootop/mi-gpt",
@ -25,19 +25,19 @@
"scripts": {
"start": "node ./app.js",
"dev": "node --env-file=.env ./app.js",
"build": "npx -y prisma generate && tsup",
"build": "npx -y prisma generate && rm -rf dist && tsup",
"db:gen": "npx -y prisma migrate dev --name init",
"db:reset": "rm -f .mi.json .bot.json prisma/app.db prisma/app.db-journal",
"prepublish": "npm run build",
"postinstall": "npx -y prisma migrate dev --name hello"
},
"dependencies": {
"@prisma/client": "^5.14.0",
"fs-extra": "^11.2.0",
"https-proxy-agent": "^7.0.4",
"mi-service-lite": "^3.0.0",
"openai": "^4.51.0",
"mi-service-lite": "^3.1.0",
"openai": "^4.56.0",
"prisma": "^5.14.0",
"socks-proxy-agent": "^8.0.3"
"proxy-agent": "^6.4.0"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",

View File

@ -14,21 +14,18 @@ importers:
fs-extra:
specifier: ^11.2.0
version: 11.2.0
https-proxy-agent:
specifier: ^7.0.4
version: 7.0.4
mi-service-lite:
specifier: ^3.0.0
version: 3.0.0
specifier: ^3.1.0
version: 3.1.0
openai:
specifier: ^4.51.0
version: 4.51.0
specifier: ^4.56.0
version: 4.56.0
prisma:
specifier: ^5.14.0
version: 5.14.0
socks-proxy-agent:
specifier: ^8.0.3
version: 8.0.3
proxy-agent:
specifier: ^6.4.0
version: 6.4.0
devDependencies:
'@types/fs-extra':
specifier: ^11.0.4
@ -473,6 +470,9 @@ packages:
cpu: [x64]
os: [win32]
'@tootallnate/quickjs-emscripten@0.23.0':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -554,15 +554,23 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
ast-types@0.13.4:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.7.2:
resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
axios@1.7.5:
resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
basic-ftp@5.0.5:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@ -610,6 +618,10 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
data-uri-to-buffer@6.0.2:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'}
debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@ -619,6 +631,10 @@ packages:
supports-color:
optional: true
degenerator@5.0.1:
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
engines: {node: '>= 14'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@ -650,6 +666,24 @@ packages:
engines: {node: '>=12'}
hasBin: true
escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
engines: {node: '>=6.0'}
hasBin: true
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
@ -709,6 +743,10 @@ packages:
get-tsconfig@4.7.5:
resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==}
get-uri@6.0.3:
resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==}
engines: {node: '>= 14'}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -725,10 +763,18 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.4:
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.5:
resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
engines: {node: '>= 14'}
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@ -803,6 +849,10 @@ packages:
resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
engines: {node: 14 || >=16.14}
lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
@ -813,8 +863,8 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
mi-service-lite@3.0.0:
resolution: {integrity: sha512-Fbz3lGPNp1Jbqqlj4EK1vya9zj3WCWXeW6+mnpcQi9RTMMPmGXC+126HHmw8WWUWj4G0tNHHP/ApHMCAIzzENQ==}
mi-service-lite@3.1.0:
resolution: {integrity: sha512-WOMK8poZZ4nvXezETGdJHiqOOMON/+8prv8Hi9kjODBGCkdUtp+Q1w9OJRYxZUiB+ZaH5f9okaqAq5TBRpg1VA==}
engines: {node: '>=16'}
micromatch@4.0.5:
@ -850,6 +900,10 @@ packages:
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
netmask@2.0.2:
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
engines: {node: '>= 0.4.0'}
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@ -879,9 +933,22 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
openai@4.51.0:
resolution: {integrity: sha512-UKuWc3/qQyklqhHM8CbdXCv0Z0obap6T0ECdcO5oATQxAbKE5Ky3YCXFQY207z+eGG6ez4U9wvAcuMygxhmStg==}
openai@4.56.0:
resolution: {integrity: sha512-zcag97+3bG890MNNa0DQD9dGmmTWL8unJdNkulZzWRXrl+QeD+YkBI4H58rJcwErxqGK6a0jVPZ4ReJjhDGcmw==}
hasBin: true
peerDependencies:
zod: ^3.23.8
peerDependenciesMeta:
zod:
optional: true
pac-proxy-agent@7.0.2:
resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==}
engines: {node: '>= 14'}
pac-resolver@7.0.1:
resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==}
engines: {node: '>= 14'}
pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
@ -923,6 +990,10 @@ packages:
engines: {node: '>=16.13'}
hasBin: true
proxy-agent@6.4.0:
resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==}
engines: {node: '>= 14'}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@ -983,10 +1054,18 @@ packages:
resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==}
engines: {node: '>= 14'}
socks-proxy-agent@8.0.4:
resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==}
engines: {node: '>= 14'}
socks@2.8.3:
resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==}
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
@ -1057,6 +1136,9 @@ packages:
'@swc/wasm':
optional: true
tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
tsup@8.0.2:
resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==}
engines: {node: '>=18'}
@ -1096,10 +1178,6 @@ packages:
v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
@ -1403,6 +1481,8 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.17.2':
optional: true
'@tootallnate/quickjs-emscripten@0.23.0': {}
'@tsconfig/node10@1.0.11':
optional: true
@ -1481,9 +1561,13 @@ snapshots:
array-union@2.1.0: {}
ast-types@0.13.4:
dependencies:
tslib: 2.6.3
asynckit@0.4.0: {}
axios@1.7.2:
axios@1.7.5:
dependencies:
follow-redirects: 1.15.6
form-data: 4.0.0
@ -1493,6 +1577,8 @@ snapshots:
balanced-match@1.0.2: {}
basic-ftp@5.0.5: {}
binary-extensions@2.3.0: {}
brace-expansion@2.0.1:
@ -1543,10 +1629,18 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
data-uri-to-buffer@6.0.2: {}
debug@4.3.4:
dependencies:
ms: 2.1.2
degenerator@5.0.1:
dependencies:
ast-types: 0.13.4
escodegen: 2.1.0
esprima: 4.0.1
delayed-stream@1.0.0: {}
diff@4.0.2:
@ -1614,6 +1708,20 @@ snapshots:
'@esbuild/win32-ia32': 0.20.2
'@esbuild/win32-x64': 0.20.2
escodegen@2.1.0:
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionalDependencies:
source-map: 0.6.1
esprima@4.0.1: {}
estraverse@5.3.0: {}
esutils@2.0.3: {}
event-target-shim@5.0.1: {}
execa@5.1.1:
@ -1679,6 +1787,15 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
get-uri@6.0.3:
dependencies:
basic-ftp: 5.0.5
data-uri-to-buffer: 6.0.2
debug: 4.3.4
fs-extra: 11.2.0
transitivePeerDependencies:
- supports-color
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@ -1702,6 +1819,13 @@ snapshots:
graceful-fs@4.2.11: {}
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.4:
dependencies:
agent-base: 7.1.1
@ -1709,6 +1833,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
transitivePeerDependencies:
- supports-color
human-signals@2.1.0: {}
humanize-ms@1.2.1:
@ -1766,6 +1897,8 @@ snapshots:
lru-cache@10.2.2: {}
lru-cache@7.18.3: {}
make-error@1.3.6:
optional: true
@ -1773,9 +1906,9 @@ snapshots:
merge2@1.4.1: {}
mi-service-lite@3.0.0:
mi-service-lite@3.1.0:
dependencies:
axios: 1.7.2
axios: 1.7.5
pako: 2.1.0
transitivePeerDependencies:
- debug
@ -1809,6 +1942,8 @@ snapshots:
object-assign: 4.1.1
thenify-all: 1.6.0
netmask@2.0.2: {}
node-domexception@1.0.0: {}
node-fetch@2.7.0:
@ -1827,7 +1962,7 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
openai@4.51.0:
openai@4.56.0:
dependencies:
'@types/node': 18.19.33
'@types/node-fetch': 2.6.11
@ -1836,10 +1971,27 @@ snapshots:
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
web-streams-polyfill: 3.3.3
transitivePeerDependencies:
- encoding
pac-proxy-agent@7.0.2:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.1
debug: 4.3.4
get-uri: 6.0.3
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.5
pac-resolver: 7.0.1
socks-proxy-agent: 8.0.4
transitivePeerDependencies:
- supports-color
pac-resolver@7.0.1:
dependencies:
degenerator: 5.0.1
netmask: 2.0.2
pako@2.1.0: {}
path-key@3.1.1: {}
@ -1866,6 +2018,19 @@ snapshots:
dependencies:
'@prisma/engines': 5.14.0
proxy-agent@6.4.0:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
lru-cache: 7.18.3
pac-proxy-agent: 7.0.2
proxy-from-env: 1.1.0
socks-proxy-agent: 8.0.3
transitivePeerDependencies:
- supports-color
proxy-from-env@1.1.0: {}
punycode@2.3.1: {}
@ -1930,11 +2095,22 @@ snapshots:
transitivePeerDependencies:
- supports-color
socks-proxy-agent@8.0.4:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
socks: 2.8.3
transitivePeerDependencies:
- supports-color
socks@2.8.3:
dependencies:
ip-address: 9.0.5
smart-buffer: 4.2.0
source-map@0.6.1:
optional: true
source-map@0.8.0-beta.0:
dependencies:
whatwg-url: 7.1.0
@ -2014,6 +2190,8 @@ snapshots:
yn: 3.1.1
optional: true
tslib@2.6.3: {}
tsup@8.0.2(ts-node@10.9.2(@types/node@20.12.12)(typescript@5.4.5))(typescript@5.4.5):
dependencies:
bundle-require: 4.1.0(esbuild@0.19.12)
@ -2052,8 +2230,6 @@ snapshots:
v8-compile-cache-lib@3.0.1:
optional: true
web-streams-polyfill@3.3.3: {}
web-streams-polyfill@4.0.0-beta.3: {}
webidl-conversions@3.0.1: {}

View File

@ -10,49 +10,18 @@ const kDefaultMaster = {
name: "陆小千",
profile: `
`,
`.trim(),
};
const kDefaultBot = {
name: "傻妞",
profile: `
##
##
##
2060
##
-
2060
-
-
-
-
`,
`.trim(),
};
interface IBotIndex {

View File

@ -1,5 +1,6 @@
import { Memory, Message, Room, User } from "@prisma/client";
import { firstOf, lastOf } from "../../../utils/base";
import { Logger } from "../../../utils/log";
import { MemoryCRUD } from "../../db/memory";
import { LongTermMemoryCRUD } from "../../db/memory-long-term";
import { ShortTermMemoryCRUD } from "../../db/memory-short-term";
@ -15,6 +16,7 @@ export class MemoryManager {
* owner
*/
private owner?: User;
private _logger = Logger.create({ tag: "Memory" });
constructor(room: Room, owner?: User) {
this.room = room;
@ -118,6 +120,7 @@ export class MemoryManager {
lastMemory,
});
if (!newMemory) {
this._logger.error("💀 生成短期记忆失败");
return false;
}
const res = await ShortTermMemoryCRUD.addOrUpdate({
@ -151,6 +154,7 @@ export class MemoryManager {
lastMemory,
});
if (!newMemory) {
this._logger.error("💀 生成长期记忆失败");
return false;
}
const res = await LongTermMemoryCRUD.addOrUpdate({

View File

@ -1,6 +1,7 @@
import { LongTermMemory, ShortTermMemory } from "@prisma/client";
import { jsonDecode, lastOf } from "../../../utils/base";
import { lastOf } from "../../../utils/base";
import { buildPrompt } from "../../../utils/string";
import { cleanJsonAndDecode } from "../../../utils/parse";
import { openai } from "../../openai";
import { MessageContext } from "../conversation";
@ -67,6 +68,6 @@ export class LongTermMemoryAgent {
shortTermMemory: lastOf(newMemories)!.text,
}),
});
return jsonDecode(res?.content)?.longTermMemories?.toString();
return cleanJsonAndDecode(res?.content)?.longTermMemories?.toString();
}
}

View File

@ -1,5 +1,5 @@
import { Memory, Message, ShortTermMemory, User } from "@prisma/client";
import { jsonDecode } from "../../../utils/base";
import { cleanJsonAndDecode } from "../../../utils/parse";
import { buildPrompt, formatMsg } from "../../../utils/string";
import { openai } from "../../openai";
import { MessageContext } from "../conversation";
@ -78,6 +78,6 @@ export class ShortTermMemoryAgent {
.join("\n"),
}),
});
return jsonDecode(res?.content)?.shortTermMemories?.toString();
return cleanJsonAndDecode(res?.content)?.shortTermMemories?.toString();
}
}

View File

@ -9,6 +9,7 @@ import { withDefault } from "../utils/base";
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions";
import { Logger } from "../utils/log";
import { kProxyAgent } from "./proxy";
import { isNotEmpty } from "../utils/is";
export interface ChatOptions {
user: string;
@ -18,6 +19,7 @@ export interface ChatOptions {
jsonMode?: boolean;
requestId?: string;
trace?: boolean;
enableSearch?: boolean;
}
class OpenAIClient {
@ -68,8 +70,8 @@ class OpenAIClient {
`🔥 onAskAI\n🤖 System: ${system ?? "None"}\n😊 User: ${user}`.trim()
);
}
const systemMsg: ChatCompletionMessageParam[] = system
? [{ role: "system", content: system }]
const systemMsg: ChatCompletionMessageParam[] = isNotEmpty(system)
? [{ role: "system", content: system! }]
: [];
let signal: AbortSignal | undefined;
if (requestId) {
@ -114,14 +116,15 @@ class OpenAIClient {
onStream,
trace = false,
model = this.deployment ?? kEnvs.OPENAI_MODEL ?? "gpt-4o",
enableSearch = kEnvs.QWEN_ENABLE_SEARCH,
} = options;
if (trace && this.traceInput) {
this._logger.log(
`🔥 onAskAI\n🤖 System: ${system ?? "None"}\n😊 User: ${user}`.trim()
);
}
const systemMsg: ChatCompletionMessageParam[] = system
? [{ role: "system", content: system }]
const systemMsg: ChatCompletionMessageParam[] = isNotEmpty(system)
? [{ role: "system", content: system! }]
: [];
const stream = await this._client!.chat.completions.create({
model,
@ -129,6 +132,7 @@ class OpenAIClient {
stream: true,
messages: [...systemMsg, { role: "user", content: user }],
response_format: jsonMode ? { type: "json_object" } : undefined,
...(enableSearch && { enable_search: true })
}).catch((e) => {
this._logger.error("LLM 响应异常", e);
return null;

View File

@ -1,11 +1,3 @@
import { HttpsProxyAgent } from "https-proxy-agent";
import { SocksProxyAgent } from "socks-proxy-agent";
import { ProxyAgent } from "proxy-agent";
const httpProxy = process.env.HTTP_PROXY;
const socksProxy = process.env.SOCKS_PROXY;
export const kProxyAgent = httpProxy
? new HttpsProxyAgent(httpProxy)
: socksProxy
? new SocksProxyAgent(socksProxy)
: null;
export const kProxyAgent = new ProxyAgent();

View File

@ -140,7 +140,7 @@ export class AISpeaker extends Speaker {
async enterKeepAlive() {
if (!this.streamResponse) {
await this.response({ text: "流式响应已关闭,无法进入唤醒模式" });
await this.response({ text: "您已关闭流式响应(streamResponse),无法使用连续对话模式" });
return;
}
// 回应

View File

@ -5,7 +5,8 @@ import {
getMiIOT,
getMiNA,
} from "mi-service-lite";
import { clamp, jsonEncode, sleep } from "../../utils/base";
import { clamp, sleep } from "../../utils/base";
import { jsonEncode } from "../../utils/parse";
import { Logger } from "../../utils/log";
import { StreamResponse } from "./stream";
import { kAreYouOK } from "../../utils/string";
@ -101,6 +102,7 @@ export class BaseSpeaker {
constructor(config: BaseSpeakerConfig) {
this.config = config;
this.config.timeout = config.timeout ?? 5000;
const {
debug = false,
streamResponse = true,
@ -367,7 +369,7 @@ export class BaseSpeaker {
if (isOk === "break") {
break; // 获取设备状态异常
}
if (res && playing.status !== "playing") {
if (res != null && playing.status !== "playing") {
break;
}
await sleep(this.checkInterval);
@ -395,7 +397,9 @@ export class BaseSpeaker {
switch (tts) {
case "custom":
const _text = encodeURIComponent(ttsText);
const url = `${process.env.TTS_BASE_URL}/tts.mp3?speaker=${speaker}&text=${_text}`;
const url = `${process.env.TTS_BASE_URL}/tts.mp3?speaker=${
speaker || ""
}&text=${_text}`;
res = await play({ url });
break;
case "xiaoai":

View File

@ -199,7 +199,7 @@ export class Speaker extends BaseSpeaker {
private async _fetchFirstMessage() {
const msgs = await this.getMessages({
limit: 1,
filterTTS: false,
filterAnswer: false,
});
this.currentQueryMsg = msgs[0];
}
@ -285,23 +285,26 @@ export class Speaker extends BaseSpeaker {
async getMessages(options?: {
limit?: number;
timestamp?: number;
filterTTS?: boolean;
filterAnswer?: boolean;
}): Promise<QueryMessage[]> {
const filterTTS = options?.filterTTS ?? true;
const filterAnswer = options?.filterAnswer ?? true;
const conversation = await this.MiNA!.getConversations(options);
this._lastConversation = conversation;
let records = conversation?.records ?? [];
if (filterTTS) {
if (filterAnswer) {
// 过滤有小爱回答的消息
records = records.filter(
(e) => e.answers.length > 0 && e.answers.some((e) => e.type === "TTS")
(e) =>
["TTS", "LLM"].includes(e.answers[0]?.type) && // 过滤 TTS 和 LLM 消息
e.answers.length === 1 // 播放音乐时会有 TTS、Audio 两个 Answer
);
}
return records.map((e) => {
const ttsAnswer = e.answers.find((e) => e.type === "TTS") as any;
const msg: any = e.answers[0];
const answer = msg?.tts?.text?.trim() ?? msg?.llm?.text?.trim();
return {
answer,
text: e.query,
answer: ttsAnswer?.tts?.text?.trim(),
timestamp: e.time,
};
});

View File

@ -1,4 +1,5 @@
import { isEmpty } from "./is";
import { jsonEncode } from "./parse"
export function timestamp() {
return new Date().getTime();
@ -87,24 +88,6 @@ export function toSet<T = any>(datas: T[], byKey?: (e: T) => any) {
return Array.from(new Set(datas));
}
export function jsonEncode(obj: any, options?: { prettier?: boolean }) {
const { prettier } = options ?? {};
try {
return prettier ? JSON.stringify(obj, undefined, 4) : JSON.stringify(obj);
} catch (error) {
return undefined;
}
}
export function jsonDecode(json: string | null | undefined) {
if (json == undefined) return undefined;
try {
return JSON.parse(json!);
} catch (error) {
return undefined;
}
}
export function withDefault<T = any>(e: any, defaultValue: T): T {
return isEmpty(e) ? defaultValue : e;
}

View File

@ -6,4 +6,8 @@ export const kEnvs: Partial<{
OPENAI_API_KEY: string;
AZURE_OPENAI_API_KEY: string;
AZURE_OPENAI_DEPLOYMENT: string;
}> = process.env as any;
QWEN_ENABLE_SEARCH: boolean;
}> = {
...process.env,
QWEN_ENABLE_SEARCH: process.env.QWEN_ENABLE_SEARCH === 'true'
} as any;

View File

@ -1,7 +1,7 @@
import fs from "fs-extra";
import path from "path";
import { jsonDecode, jsonEncode } from "./base";
import { jsonDecode, jsonEncode } from "./parse";
export const kRoot = process.cwd();

32
src/utils/parse.ts Normal file
View File

@ -0,0 +1,32 @@
/**
* json字符串前后的其他字符,
*/
export function cleanJsonAndDecode(input: string | undefined | null) {
if (input == undefined) return undefined;
const pattern = /(\{[\s\S]*?"\s*:\s*[\s\S]*?})/;
const match = input.match(pattern);
if (!match) {
return undefined;
}
return jsonDecode(match[0]);
}
export function jsonEncode(obj: any, options?: { prettier?: boolean }) {
const { prettier } = options ?? {};
try {
return prettier ? JSON.stringify(obj, undefined, 4) : JSON.stringify(obj);
} catch (error) {
return undefined;
}
}
export function jsonDecode(json: string | null | undefined) {
if (json == undefined) return undefined;
try {
return JSON.parse(json!);
} catch (error) {
return undefined;
}
}

View File

@ -1,51 +0,0 @@
import { MyBot } from "../src/services/bot";
import { AISpeaker } from "../src/services/speaker/ai";
export async function testMyBot() {
// await testStreamResponse();
await testRunBot();
}
async function testRunBot() {
const name = "傻妞";
const speaker = new AISpeaker({
name,
tts: "custom",
userId: process.env.MI_USER!,
password: process.env.MI_PASS!,
did: process.env.MI_DID,
});
const bot = new MyBot({
speaker,
bot: {
name,
profile: `性别女,性格乖巧可爱,喜欢搞怪,爱吃醋。`,
},
master: {
name: "陆小千",
profile: `性别男,善良正直,总是舍己为人,是傻妞的主人。`,
},
});
const res = await bot.run();
console.log("✅ done");
}
async function testStreamResponse() {
const stream = await MyBot.chatWithStreamResponse({
user: "地球为什么是圆的?",
onFinished: (text) => {
console.log("\nFinal result 111:\n", text);
},
});
const config: any = {
userId: process.env.MI_USER!,
password: process.env.MI_PASS!,
did: process.env.MI_DID,
tts: "custom",
};
const speaker = new AISpeaker(config);
await speaker.initMiServices();
await speaker.response({ stream });
const res = await stream.getFinalResult();
console.log("\nFinal result 222:\n", res);
}

View File

@ -1,58 +0,0 @@
import { assert } from "console";
import {
ConversationManager,
MessageContext,
} from "../src/services/bot/conversation";
import { println } from "../src/utils/base";
import { MessageCRUD } from "../src/services/db/message";
export async function testDB() {
const manager = new ConversationManager({
bot: {
name: "小爱同学",
profile: "我是小爱同学,机器人",
},
master: {
name: "陆小千",
profile: "我是陆小千,人类",
},
room: {
name: "客厅",
description: "陆小千的客厅,小爱同学放在角落里",
},
});
const { room, bot, master, memory } = await manager.get();
assert(room, "❌ 初始化用户失败");
const ctx = { bot, master, room } as MessageContext;
let message = await manager.onMessage(ctx, {
sender: master!,
text: "你好!",
});
assert(message?.text === "你好!", "❌ 插入消息失败");
message = await manager.onMessage(ctx, {
sender: bot!,
text: "你好!很高兴认识你",
});
await manager.onMessage(ctx, {
sender: master!,
text: "你是谁?",
});
await manager.onMessage(ctx, {
sender: bot!,
text: "我是小爱同学,你可以叫我小爱!",
});
const messages = await manager.getMessages({ take: 100 });
assert(messages.length === 4, "❌ 查询消息数量异常");
assert(messages[0].text === "你好!", "❌ 查询消息排序异常");
const newMessages = await MessageCRUD.gets({
take: 100,
cursorId: message!.id,
order: "asc",
});
assert(newMessages.length === 2, "❌ 查询消息数量异常(游标)");
assert(
newMessages[1].text === "我是小爱同学,你可以叫我小爱!",
"❌ 查询消息排序异常(游标)"
);
println("✅ hello world");
}

View File

@ -1,20 +1,10 @@
import { println } from "../src/utils/base";
import { kBannerASCII } from "../src/utils/string";
import { testDB } from "./db";
import { testSpeaker } from "./speaker";
import { testOpenAI } from "./openai";
import { testMyBot } from "./bot";
import { testLog } from "./log";
import { testMiGPT } from "./migpt";
import { MiGPT } from "../src";
// @ts-ignore
import config from "../.migpt.js";
async function main() {
// println(kBannerASCII);
// testDB();
// testSpeaker();
// testOpenAI();
// testMyBot();
// testLog();
testMiGPT();
const client = MiGPT.create(config);
await client.start();
}
main();

View File

@ -1,9 +0,0 @@
import { Logger } from "../src/utils/log";
export function testLog() {
Logger.log("你好", ["世界"], { hello: "world!" });
Logger.success("你好", ["世界"], { hello: "world!" });
Logger.error("你好", ["世界"], { hello: "world!" });
Logger.assert(true, "你好 111", ["世界"], { hello: "world!" });
Logger.assert(false, "你好 222", ["世界"], { hello: "world!" });
}

View File

@ -1,33 +0,0 @@
import { MiGPT } from "../src";
const botProfile = `
`;
const masterProfile = `
`;
export async function testMiGPT() {
const client = MiGPT.create({
speaker: {
userId: process.env.MI_USER!,
password: process.env.MI_PASS!,
did: process.env.MI_DID,
streamResponse: true,
},
bot: {
name: "傻妞",
profile: botProfile,
},
master: {
name: "陆小千",
profile: masterProfile,
},
});
await client.start();
}

View File

@ -1,37 +0,0 @@
import { randomUUID } from "crypto";
import { openai } from "../src/services/openai";
export async function testOpenAI() {
await testChat();
// await testStreamChat();
}
async function testChat() {
const res = await openai.chat({ user: "地球为什么是圆的?" });
console.log("\nFinal result:\n", res?.content);
}
async function testStreamChat() {
const requestId = randomUUID();
const res = await openai.chatStream({
requestId,
user: "地球为什么是圆的?",
onStream: (text) => {
console.log(text);
},
});
console.log("\nFinal result:\n", res);
}
async function testAbortStreamChat() {
const requestId = randomUUID();
const res = await openai.chatStream({
requestId,
user: "hello!",
onStream: (text) => {
console.log(text);
openai.cancel(requestId);
},
});
console.log("xxx", res);
}

View File

@ -1,115 +0,0 @@
import { AISpeaker } from "../src/services/speaker/ai";
import { StreamResponse } from "../src/services/speaker/stream";
import { sleep } from "../src/utils/base";
export async function testSpeaker() {
const speaker = new AISpeaker({
userId: process.env.MI_USER!,
password: process.env.MI_PASS!,
did: process.env.MI_DID,
tts: "xiaoai",
debug: true,
});
await speaker.initMiServices();
// await testAISpeakerStatus(speaker);
// await testSpeakerResponse(speaker);
await testSpeakerStreamResponse(speaker);
// await testSpeakerGetMessages(speaker);
// await testSwitchSpeaker(speaker);
// await testSpeakerUnWakeUp(speaker);
// await testAISpeaker(speaker);
}
async function testAISpeakerStatus(speaker: AISpeaker) {
const playingCommand = [5, 3, 1];
const res1 = await speaker.MiIOT!.getProperty(
playingCommand[0],
playingCommand[1]
);
const res2 = await speaker.MiNA!.getStatus();
console.log("finished");
}
async function testAISpeaker(speaker: AISpeaker) {
speaker.askAI = async (msg) => {
return { text: "你说:" + msg.text };
};
await speaker.run();
console.log("finished");
}
async function testSpeakerUnWakeUp(speaker: AISpeaker) {
await speaker.wakeUp();
await sleep(1000);
await speaker.unWakeUp();
console.log("hello");
}
async function testSwitchSpeaker(speaker: AISpeaker) {
await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" });
const success = await speaker.switchSpeaker("魅力苏菲");
console.log("switchSpeaker 魅力苏菲", success);
await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" });
console.log("hello");
}
async function testSpeakerGetMessages(speaker: AISpeaker) {
let msgs = await speaker.getMessages({ filterTTS: true });
console.log("filterTTS msgs", msgs);
msgs = await speaker.getMessages({ filterTTS: false });
console.log("no filterTTS msgs", msgs);
}
async function testSpeakerResponse(speaker: AISpeaker) {
let status = await speaker.MiNA!.getStatus();
console.log("curent status", status);
await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" });
sleep(1000);
status = await speaker.MiNA!.getStatus();
console.log("tts status", status);
}
async function testSpeakerStreamResponse(speaker: AISpeaker) {
const stream = new StreamResponse();
const text = `
###
1. ****
2. ****
3. ****西
4. ****
5. ****
###
1. ****
2. **西**西
3. ****
4. ****
5. ****
`;
const add = async (text: string) => {
stream.addResponse(text);
await sleep(100);
};
setTimeout(async () => {
for (const s of text.split("")) {
await add(s);
}
stream.finish();
});
await speaker.response({ stream });
console.log("hello!");
}