From dcb6d0feab9e069f116a43214eea4ac03e8f4058 Mon Sep 17 00:00:00 2001 From: haotian Date: Sun, 13 Apr 2025 18:46:04 +0800 Subject: [PATCH 01/17] =?UTF-8?q?update:=20=E4=BC=98=E5=8C=96=E7=B3=BB?= =?UTF-8?q?=E7=BB=9Fprompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/connection.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index fc191b6f..1345879e 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -335,10 +335,8 @@ class ConnectionHandler: def change_system_prompt(self, prompt): self.prompt = prompt - # 找到原来的role==system,替换原来的系统提示 - for m in self.dialogue.dialogue: - if m.role == "system": - m.content = prompt + # 更新系统prompt至上下文 + self.dialogue.update_system_message(self.prompt) def chat(self, query): From 13288a6b3a9c38997f648cabb652f66d4a226d54 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 14 Apr 2025 10:26:34 +0800 Subject: [PATCH 02/17] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update:添加设计公司致谢 * update:添加设计公司致谢 * update:优化文档 --- README.md | 86 +++++++++++++--------------------- docs/images/logo_bailing.png | Bin 0 -> 36089 bytes docs/images/logo_huiyuan.png | Bin 0 -> 29246 bytes docs/images/logo_qinren.png | Bin 0 -> 14354 bytes docs/images/logo_tenclass.png | Bin 0 -> 6247 bytes 5 files changed, 32 insertions(+), 54 deletions(-) create mode 100644 docs/images/logo_bailing.png create mode 100644 docs/images/logo_huiyuan.png create mode 100644 docs/images/logo_qinren.png create mode 100644 docs/images/logo_tenclass.png diff --git a/README.md b/README.md index 6feb1fc7..fee67ddd 100644 --- a/README.md +++ b/README.md @@ -145,42 +145,24 @@ server: --- -## 部署方式 🚀 +## 部署文档 -[![Banners](docs/images/banner2.png)](./docs/Deployment.md) +本项目提供两种部署方式,请根据您的具体需求选择: -### 部署文档 +#### 🚀 部署方式选择 -本项目支持`最简化`安装和`全模块`安装。请根据具体需求选择安装方式。 +| 部署方式 | 特点 | 适用场景 | Docker部署文档 | 源码部署文档 | +|---------|------|---------|---------|---------| +| **最简化安装** | 智能对话、IOT功能,数据存储在配置文件 | 低配置环境,无需数据库 | [Docker只运行Server](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) | [本地源码只运行Server](./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)| +| **全模块安装** | 智能对话、IOT、OTA、智控台,数据存储在数据库 | 完整功能体验 |[Docker运行全模块](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) | [本地源码运行全模块](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) | -`最简化`适合在较低资源配置中运行,无需数据库。[请参考-最简化安装架构图](./docs/images/deploy1.png) - -`全模块`适合想体验完整功能,过程复杂。[请参考-全模块安装架构图](./docs/images/deploy2.png) - -#### 1、 最简化安装 - -`最简化`安装包含:智能对话、IOT等功能,数据存储在`配置文件`。我们提供了两种安装教程,如果你决定使用最简化安装,可选择`Docker只运行Server`或`本地源码只运行Server`任意一种方式。 - -##### 1.1. Docker只运行Server - -你可以使用编译好的docker镜像安装,[点击Docker只运行Server](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)。优点:减少环境配置的烦恼;缺点:版本可能有点旧,镜像文件较大。 - -##### 2.2. 本地源码只运行Server - -你可以以本地源码的方式运行,[点击本地源码只运行Server](./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)。优点:可以随时更新最新代码;缺点:安装Conda环境较为复杂。 - -#### 2、 全模块安装 - -`全模块`安装包含:智能对话、IOT、OTA、智控台,数据存储在`数据库`中。我们提供了两种安装教程,如果你决定使用全模块安装,可选择`Docker运行全模块`或`本地源码运行全模块`任意一种方式。 - -##### 2.1. Docker运行全模块 - -你可以使用编译好的docker镜像安装,[点击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)。优点:减少环境配置的烦恼;缺点:版本可能有点旧,镜像文件较大。 - -##### 2.2. 本地源码运行全模块 - -你可以以本地源码的方式运行,[点击本地源码运行全模块](./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)。优点:可以随时更新最新代码;缺点:安装Java、Node.js、Conda环境较为复杂。 +> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据 +``` +智控台地址: https://2662r3426b.vicp.fun +OTA接口地址: htts://2662r3426b.vicp.fun/xiaozhi/ota/ +Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ +``` --- ## 常见问题 ❓ @@ -206,31 +188,22 @@ server: ### 已实现 ✅ -- **通信协议** - 基于 `xiaozhi-esp32` 协议,通过 WebSocket 实现数据交互。 -- **对话交互** - 支持唤醒对话、手动对话及实时打断。长时间无对话时自动休眠 -- **意图识别** - 支持使用LLM意图识别、function call函数调用,减少硬编码意图判断 -- **多语言识别** - 支持国语、粤语、英语、日语、韩语(默认使用 FunASR)。 -- **LLM 模块** - 支持灵活切换 LLM 模块,默认使用 ChatGLMLLM,也可选用阿里百炼、DeepSeek、Ollama 等接口。 -- **TTS 模块** - 支持 EdgeTTS(默认)、火山引擎豆包 TTS 等多种 TTS 接口,满足语音合成需求。 -- **记忆功能** - 支持超长记忆、本地总结记忆、无记忆三种模式,满足不同场景需求。 -- **IOT功能** - 支持管理注册设备IOT功能,支持基于对话上下文语境下的智能物联网控制。 -- **智控台** - 提供Web管理界面,支持智能体管理、用户管理、系统配置等功能,方便管理员和用户进行管理。 +| 功能模块 | 描述 | +|---------|------| +| 通信协议 | 基于 `xiaozhi-esp32` 协议,通过 WebSocket 实现数据交互 | +| 对话交互 | 支持唤醒对话、手动对话及实时打断。长时间无对话时自动休眠 | +| 意图识别 | 支持使用LLM意图识别、function call函数调用,减少硬编码意图判断 | +| 多语言识别 | 支持国语、粤语、英语、日语、韩语(默认使用 FunASR) | +| LLM 模块 | 支持灵活切换 LLM 模块,默认使用 ChatGLMLLM,也可选用阿里百炼、DeepSeek、Ollama 等接口 | +| TTS 模块 | 支持 EdgeTTS(默认)、火山引擎豆包 TTS 等多种 TTS 接口,满足语音合成需求 | +| 记忆功能 | 支持超长记忆、本地总结记忆、无记忆三种模式,满足不同场景需求 | +| IOT功能 | 支持管理注册设备IOT功能,支持基于对话上下文语境下的智能物联网控制 | +| 智控台 | 提供Web管理界面,支持智能体管理、用户管理、系统配置等功能,方便管理员和用户进行管理 | ### 正在开发 🚧 -- 多种心情模式 - -想了解具体开发进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3) +想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3) 如果你是一名软件开发者,这里有一份[《致开发者的公开信》](docs/contributor_open_letter.md),欢迎加入! @@ -298,10 +271,15 @@ server: ## 鸣谢 🙏 -- 本项目受 [百聆语音对话机器人](https://github.com/wwbin2017/bailing) 启发,并在其基础上实现。 -- 感谢 [十方融海](https://www.tenclass.com/) 对小智通讯协议提供的详尽文档支持。 +| Logo | 项目/公司 | 说明 | +|:---:|:---:|:---| +| | [百聆语音对话机器人](https://github.com/wwbin2017/bailing) | 本项目受[百聆语音对话机器人](https://github.com/wwbin2017/bailing)启发,并在其基础上实现 | +| | [十方融海](https://www.tenclass.com/) | 感谢[十方融海](https://www.tenclass.com/)对小智通讯协议提供的详尽文档支持 | +| | [汇远设计](http://ui.kwd988.net/) | 感谢[汇远设计](http://ui.kwd988.net/)为本项目提供专业视觉解决方案,用其服务超千家企业的设计实战经验,赋能本项目产品用户体验 | +| | [西安勤人信息科技](https://www.029app.com/) | 感谢[西安勤人信息科技](https://www.029app.com/)深化本项目视觉体系,确保整体设计风格在多场景应用中的一致性和扩展性 | + diff --git a/docs/images/logo_bailing.png b/docs/images/logo_bailing.png new file mode 100644 index 0000000000000000000000000000000000000000..22ab6e11f3b27304f19ecb743798a610ef3969c2 GIT binary patch literal 36089 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 VwjHcBY0m%v002ovPDHLkV1fz;iZ}oO literal 0 HcmV?d00001 diff --git a/docs/images/logo_huiyuan.png b/docs/images/logo_huiyuan.png new file mode 100644 index 0000000000000000000000000000000000000000..6e441913c88df32440a7b14932f9ff081249d125 GIT binary patch literal 29246 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 **Shz8+QdC4H4Hbu#Vv+Q zUhNcg33VZe9*PlYAt0VCtj>6PeueYCe;G#)k1({paOx5BuXb!T0gl zliu1juwsSN*OYdZ6><{m_DG8Csl$Z=K3_#|XALg8{8ufl!6-YxR+Z?}kEU*&YDcUE ziw(fi(%Jv&|Nigru-`d_&DrwXxRvwm7r96sJ8cp#HkCjLJog$QKk`wJ)-9S5S>4b4 z_kRyhJniunN)G$>)c%Dl$`(se;sFy?|E)$_&^q*Sak3!I|61(HwhYGbnSA1ze)&s;4bN5g4Edk6Cx>z&xfhfT_D%ic#o6XLnnq%2yH9IssbYGw|z z$r*260P3*A1}>bj7(Dr?B(rF?efkgTw1y_qySAuYGMtZ95l1a{ujzJT$ycZ)03*?$sK{cSgCV*Sip@SQyhceF@*5L+LcwB>u3q-x9 z>?BR|$T<)ysl?$e1m8Mf;ydm*w~24baEkuOC!^61&d$ke?!8;O9`A6+I}R7V@P&Qb zqTI4z!i({wC=JlhcA&unwS*|$B5jj$gB#v(IQ3x<2P|i}#zOkCX%ucVj*zcn143#` z#5^?@4!#->@1VjHDo>&aGhTci-U(Q3&LRAA?CBp~^b;@d=_bj8AZ$1!^fG|DHjL$W z4vg>gJ0<=}AYDGwd~7gzIh z<&L;b!`n41hKfxvQs73)E1gn@%=yoM{^4PdeAGyzcHf4ls{Zs({-hFi+>&u^X8TbA z%J9|TLD4D4DjBVqrtRo%^ zE>%{}ih2p>RfL|NF_xrw2{qfh zpqYFXq6Dk8Y^DBuoymZhY~yJSfwvkt?@kOYhdyPw$B8Em#~;7*r>jpq33}%_Pr{() z0Nv&M^WUbW%5SbiY(CO?jcZ=RiMCDkg293R6(8>n&$T_%o9GQ4BwQC>;a6oRdagmn zI`MetHYRb5O>i00W9aRZL3canc@-e9IDxE>sW{Y7tXpuk+pJM9P!eXO0u5vc2l0J}lNUZXeBK$S5C8oB_fOldXNzfmM@1ZB6SooRL`EHYuA)Zi?O<-gQp|Dr zAPx3rsRhI^m$LmLspk?{sH;pgmJgedt4GA8WnDCBA$3`CH!_n$`b=39H#% zTkljp0ndBh^A68B{fvRWIJtVP*`}Pm<1t!l;=mX+{}OYhwx}hc!`m!|;E^!7F0JXM zgLv~l6Mp{F93;)M(kF%*1U42=*z!&Gf+jisLjAU!3nSpgv~FNp6cob3sEZ{R2)X7( zi@jZzOdMRsTGKsV_>n}~Z%6VqJQL4n$Hy(t z(_LX!E1&+( z5@;os3fTH{$2;9=xZs5@G->HS{^LJ}7rx;6!z*9$@<~34{IFF;@bt^w?g(1ioU5z+ z6hP%pwJv_~OAOC`?sJFpA=x{b^79D#;?E(N5E(ChMxmPiz)pmm>u6o!tT>T#&G{ZW z_40|BO!B1Vi#F(oeF@L#SNJoIMeBnwKuEiuQ}wn9z75o|(#}OOU2}1HVJ(e^>on!K zAmSQ83z7_9+4-$J7vx?PtXFMn)?T?yy4vGGFzHqDWxc&G5 znu#Amc_qv_SXcCHI|P+zAfGd)L6G=k}%}FieLiL>cO!~=77jAa*o6mke@wKmgZFunuUm(N$ zX@qzD!`mmEFzMv`@Y}CO<&l{z|3$XVYQC--20eu1ZSjHYJ@17RR>V#T7P!1}S7JwU zKb{G`N%HbH6&7p?GS;x-iFVeh7HoBM?$d{w>=`LoRk%2R>M3SzaH!9c%RoCjY%0Oa z0C8qsR-Zd+c?A4adroh!>F1>Fy&lVM@M5;tLX>T#^)nWCG;xiC2A?EYDwL6YEGjK# zQ#v&*ys`{CmKHG=a9M;Sw*3JSjZgmjzYo9n#QEPqAvo*PD)dx@_)+wSXK^Z~wO_RH(R+rhVP{KzBQ{MlhW0tp+gy;k(( zje(`kdE8#XFU{03%gYUMC<91c)|(ex?4!i$f~vH!5S2jK-x=Jj;jl0~>)Fp5KJbAL z%*tKyN>>{0dE$x9W^5dY<`u4R#o>f|o;Zu~tYVO zH7=!C&kF%mNtav(6&-Hk^(CVgvZ#cdI02qE#3M42E%WWQu66C2VPcXErHv2U07gKn5a;7is3;gWUUnYL)Y!f_#xGlSi z02iVcC}FrFxqw|ayF>KpLuC#kx+#s{iP<$(|St>L>ae(~5C@C++oFD`vtmhmco z?vcMweiH8zp49#tAuMUf(PC*dt(H3PRr)ZNU<4Ee?oPf~*o=RorT?OvXTkZS=LWu^ zQC=Y~Q!+z)1TDbPw_yK!GN$G6?QeVA@Lv9G1NEbDZeH-b=Vf=LzV&T>VYt|LeV6HF zOh%|ED{4C?JnG>O8{Yfg_blr1XAGWm`kDG!1J0nCUK$e{mV<8isvbClF!e% zG)2NBz0Q)_jVdR7ymr)wF6+FCcf#LZ!7ohHmbGyNunM^ebRQA-YVvU~^~vLhwfewz zlxvECb_EyWMvX_}370P|JmGSiU--r0+CT9Vz{+hm=xg&2{hd>1@&5+D-T$gTe`UkB zf5pqTfi}A$aW}3h&SA|ag?xI(db+d{wE_Gw1J11t?f2r-YC%o^?BiH;-YO;UbuxcP zW?JSy{^OIwU*L8OW>@pCM5x{{eCEIYYw?lEyWH*W!?%9xw~n1zo3vLH)(}4ViBAmo zz1K;2!si={l0S=|sNm-@N|upCGqKki5bLKHNtRFE#M3vpqRylXaAsjoctB}xa8L{* zb{S7PoOHIwrS-MtXbOD4E=AVY+2$X_c!MjPA#e&18frn-FLDgDR=>19E<-oJc7{3ik#}(%2@+sMfJ> zppiik3bPJfNuzzPNbnfez`y#mEjx$+*8>;CsN~`rkvHnLaAmE*2p1;%{HryiRah6Z zq&70MB6Q{%XAF0^e#4(9tkXEbGbWp%=mJHs&EooJ?{Jhn9Y=F1+8w%D&1UdbHS&J?4)!*|Bxs`&_)uy z+uctXzU5oKC84^Sk4Ew~42$qM9?f-y`KNckYxsw^zkT>oKC(JdPQ2I2!}HEO1Mlwe zTuH`bLT*>280?E)@PdKQ;oqtGO`I_<=j2Jh|786gg7?4gePXXB`LWhSA8}wPb4WSY zxR~$uoMb|lQS0Y}j?W}}0Q1B{{Cbh(ffBi5{8$1yMVLJI@%$7 zD7N_pn}^^7*Zyj&NBXtjc;N6o-~HXXiu3Q)^nCU{{_&3wfAIUix60^z>pH#c0;?6w z4NqFV((5GJ&SKjp*BFi@=L$zQg2p7GS3pE>IPZDRJKX(*6J~9{;$?q^M4Bz-OL*y5b~7N@4Q4IK@C-^Xk_OpZd?~M<%Iu@B97|J_J7wM&kwFSm1)= z0>{{PVbl?W@Uha9-`6=uMBt2l;~QTae(B_U>3bBtWZ%2w_kaKJ6g)n8{CRML14eX0 z<~%TV{LSCSqh8ZJUO9WM9~^jF<{L=%2ly@pG2$Aw|Biiqrk(g))$p~6C7+9DpKInU zJSWfBH}Dt5cx$Y@(C5PqdcXmG9xMR@=9Gh5+BO1*GXmOpT*UmRJzLxA%eWY2v6Iw( zu_xQsw6~9W)T4$keBtx%?C|)8*S~&veg5V=Glw=dH@7#qlfCH)Z4a}zS*@{S-2AVK zafn!S2;qZi*%{aNt1&+!l^FCub%vGy0xV3lpWSeLj)nB@cgVMAk6 zg+AAl>1=Nkydt$d9Ek{cVQWchVR9jAacWUUq61MsgvB~u+VphLs+2D6*6;tuUt4{}cNZ#s6C$t8zk&Tr0c&X`s1tP{a2Gib#$?3ZFWvtou znIzi&L|gXl(+z*}Cx;*U;UAt5GU@c^J|{y10;A%hB-K$&e=+WV&pk3piIeYxA0gvn zh*$dRE_`A`fifPwbYD)JICX@cC5=HCuBmt7vw;Wx+OJM&bhNvjaKdo=JKeFyzT?h! zG9HxByG}V#$D!fKGtXZ>v#wZz#E6!}&K_wvw74EQj{c8IMieXU7mgjIZGxYlr#<6% zfzHF_pA(yx*g0n%<~pBtZgDe>y12O@W1Di3f{|a-=3=dj)QMg+(J9N@Jt^b9afg9_ zFZJEnz&j~VdGa3&AN#j|)fVx=TZzZ6rJa_v84Kyi1e;%T=csdR97z5HH3l-F*pbq! z@C+4yFJ-RKRwm4`jFN`4hh>yR_UX3CST_6o)yUW4(a2KdMw5IZMum?CPrOUN=X2AW z{ak#T)N`s72b_}RXrrtVMo?YFQk}Vj#^d>Nw5s0AU;gspsZV+GoMxdv3Qu5N1Mf0e z7_Yg{Z;Ap8We8}~F%B9TDjtKq2@)#FE1?r4`(UKF#?!<)Rh!soZ9yUHb47yZW0DvD zu8Xg{E`e_`F1@C7TG~fH`q2#GuIi=dvD-YMZK7XHOx;!6G zz3@YA1txq<$$(V1iwbbhSonP!CQbgurBWjP%#=#1xGkeZ34s>3Z5Q39AwDpO<5vKX zf!Aq|f86kwZ+Ly9vD)NMNI(7A&lxU(59o>Elg_GKOX-t(ay%CDXq-dz+(r=53Ad!6 zS%GZQvNpk3-I&6RZ(YXP1?IUT!E?bKzy}S7yc{^L9G2;U3S z$YT9F_*Ua#T;e{kXo02;7aOoO@cD(f>bKPE;9IrQ_I9taP&_bp{Et!^ zNtpeOb_#s{^IUtG9-4Ruj8cKfPA2&~F$KJ7_*WwxS9{nV>zt)Zv5GOj=b!#*@mD5q ze}_Bj52tf+a87lvI|t1;-FJTHcka0hb6@01L!bNnkx%exn~Qi{GHto$(F;7~3SA$1Vfhjw zl_;ck67~&`d(5MUyDU8OOcmZb_^ZGAOEybMgceWl2Efq9A_JSsLtu@5QObl`;JOB% zDd%H&`4b2oGdr2o&?jsr1@`z<5lv^C^#$;A40C^|gp%_Y7sL}(9`u)vZnf}zlJlMK ze8bCL$#3|MgAdYdk;CF%<}#O=m111(cK5pve~8;<8BZoDx9O-p<_!JB%z7G@TGDUC z_&g2$7YV`q$``*poOF+S46nq~K68J}gfRTEwa5PM6NlgU)n8$Ep)ldXEEi~FmkK$x zSsNthG==NBr>B(loO>wN31~1<+S*b9KX#{|Ku*#@T}knq zfm7uQSGaepQT~-d*IhM`@fsI3VWH${NO5ak0|#z;hw`YpZ+vF z&Ult0&FrRsoC7{Ch|t%Rwt-QF2r~vnLMRX%Gp8^M+8_PMM~3_T(tUQGmru{04F@*7MMJGad(pq87oD3cptIelLB`ekD@i|{XRS>tqi)k4!N^nXw(TAquDtMEKgB;hvF>tx)^QC3zbA0#HSc(c zcQ^}w`STa>+d+&n6BGT@EeILK_z?sDZoASxs1XzKCk~pfNbZrpG-io{N%er>?+=~! z*vC$Q^rYW<$U}!KUj=ixRJexI9_sQBWVSUXT;}r~y2MX{UV8-06K zc-4Bn>tAmc?i2U{CVp%4+*ER}Dpz9Hs?MaiI8g8KDjGX}QD>g{*0(Ir^Uwb5P3D!? zaKcmPB@b};nKgKCd&^rUQQPFs_U-Xg4;Q-7g(qc?B=JQ2xS6t@QlS$cGgu~OCc-R- zG*irmdI5!C`9kvrOUvdPg^%+bB{Xs@xz;J}XB`pz2xP{UzNw-ap{a)$zmrxpJNBU2ot| z@NQfp8LY^|#moNC55+HKu5d-*BaI*X*uNEK(!0J{)Qz@qWXjHbY$kfv)WYFG_*KlM^42vM6`3z@a7Z}8-s_$x4F8RvJ3iE+C%f?Em9NHodxDmq#tb!Dt*R*SMxkU`P;odL75Jov_nokTRQ4LWVM^e{*lkZzx-t{A9!^=S03@m zM+|SqTm1LF=e=_xi*zP|C*iLUegDE{(x!5Wlf3)1 zsQj`6(=U3#^YN>dSDV%Nxtrf&xD$TSGXGHZtYDHqVmi%%#uuw9e! z_M!o=amp*!WOY56n_M2ieznO!0 zpLD7;V=CrogRX)9lRy6B;eo&TpyA?|nEqqlRDay>K4G{Lo^*QbqaLZJedZvW z)Wdx~d>cMrdFVrbXYprmiS>?m{KN2;xPo_bZ^u`y@uz&RcJ*si!l?BpFMa9oikH1? zMD)&1^!pW!J_e@3B=8~iPtU%UUrBZ*}y*eIQ``fQ)KEm-tfKK%5P zF08vq4{yiM9G~)#2M?z`Vg8T1&l;g$5d-}Q9R!P~xu|F_7BzWnCadD&WEP4R9S1h} zAj^9QCnhndy1fTqqj}g{@566Qzy5X0-<;#~z>mW>>|TlIjhCJqPG|Wk{C)3x-{J56 z{_oc&cYaajnfR5wb7$dlZeJUYGB*de2;168+(_Pl?S|P95s&$OV|doHmgCH%dskeA zzu|BGcDTU}Z!`lfhrIjIyul4`IK1Jn{$_1*C)m^Y?c69F!`$m$_ik>7q(W0a?~Lan z$uN=l$~p24!!vr>*Ag$tD?3fqn5VyZ^{a;eT=uSX2cA8h}nzrWx40!Zw>6YV9fc4ZiGiah|GD2N51rXOL4nJzceWc z+V{&bZ!Hsn81-e;Mkbh=1GZ6)7hWSX*|h8=i}QC*7lq^~@S}~xTwG2$<)OomU;D@L z#KMJI@s;+qf8yH18{hQC;lm&P@bH0u`Iq5e`1-&H@WqwS=|j59;g4AGZ%JMr^+h-Q zq7>g5{MRn-Ev`6uR1>htHt^3fmgN%CzKG31buX(xLdR(?fYp!osNz@iZ++|AtbL@8 zpS`^3k6t=_{9_**K7jfC&<8)D`V*h{_;AVZyQKbD34i$OvX}j#jqj7c7ViRI|JuK( zDdv| z_^kU2pLgc)z~7qw!|8k;pU>s<3u`RW42;sA+ho#!4g`6{UVH%EdENJqjKiNnNDJYs zy3!gRcIqjbgLUtW!=x%aD5GSaG(JzGw+tcxmtDq%h9fE4Bha#t%~9(=4|u6m8Mc_r z$>9m1a)|E|w`fv1s7F!d4R;hdv`be$@{x}Wzl1C97vSoEld@F!4aLhdiQ~1~@~60; z_>5=Fza`!+{^8eOj#ZPBH+Mxl1mXdmo1z5}Ex@_qxG{6y^aUEc$Di_y@0L!w=Ly4K zy#5WtMJ{rY7IL!94>|ez=^Nj8lD{eO!yo?8@PPZ@&mZtn3C`&j{>V!%J^n?dxo=lf z^$h%KWL}YH0`XXR3ZKh=GX4O| zSHAKUumd51rW9sqRMDg9;?n^V;47M%Q87jA5U6(mf9CPtC!aXH>NT$$E_umIcKo>x z^>U3&$v7mGLaPg6FRhaRL@4hI3u{t}F-%piK-uG%S~}XhzC}a8ZP{4_UW?xyKkc;B zcBZro>38F?uLnHf0lPr>=2p!l$GUy_IcIM+?t}5`;~)RTa3B1L7H`=ciSmUnd||lP z3HQ)FeQtOqfZ~^Zce)q`{!P@|-r)|jZhaCTG`{vPUek>?pUGyO$t3~Uw@us4IN4sG zB$TWm|C!HzX83)-pUda-`MWC2qmdrjtUijNIpZP6R&7aIx}U?hqEA9x7k^?R z6`|JJs6Pel^+7SmYAoj@-RdSJW0?N+aTS{IdfD3Bb%H036bQ`9p2L;uWtLZh@yZzWUX#9xB8)y<3`2vbI8#Y|lOb!eG{_b!8?{J%2-E#O( zJd=Jn%Rj&W{lhJ9c2j(F^~1O=!EZRmi(jmP;e62tw(okkdkp74|M}USEWCY^CnhG9 z{MK&PsWeVV0bC&X7l}!{#>!7T6r2g4sfoIgZmOB zE?&vcI=_tc&+mU9enWmI7fHaTJp!prIc_VIt0nbZ+oQ^DTWu^Ouq|8dJxTB@s9VBD z5#TQ#-trc=82HW7y)N9Gk9fo*h7<33;$~MKd+orv2rm;j?~`&<@ygu^pU*n`d1pQQ z45qHIO2Vi7K|Im&&fz9MbK~L7hw1wVyb8X}t!^>=+rRy*lM0hMUj7<95uMvGcvI)qg(ePcbH z=zYS>*w-nj7<-i9b3jw|`hk5jOT6=)?;NiE<3B$9)`K3jCyD;+U;FxS#_4Aa*ZQ$* z;g|X9lN8^~G2XkeN26^ubw0$QZi7`lyPGcMY-Pp&E5XVn)x5OhF z0bVij`ScgyPn+H9R<{~{>ZgBtxbAhYJMamMO$zS~@|&`M_ji9cycADleCku5+C*(D zv&#s06UD~GM&t*KOdj#6CiknFmLTUKlEdrty6*L^Kiur+ehz>8^Sb&FeJPoyoBRm` zUh%%^jeoCu*Y7Rgn8g(`69F9J<9Yd^Dt}Ed3xC9lg?83`vFXgypA!kqI*HNN{x+rY zG*FKmBK#1Vl2y0uXX0UOQsg!9$fO^Oq;6lXj_35Rg^QrKz2&W9Qcj%u5_h= zKQqkNMesT=o~+^3)n`BZ+2OOF`z#)>`}pv#cfG6JFFkjR{K$@C-8$lRW-ZO`5s^H; zu<6(bm}_>>1#7Gk997J_oZG8k^BTiRcr5m_1OGS+-vRu8 z_(LXN#y6tn4K_x-(gTS9%f9i$HvQp?w{7H`YmTYC(yUm7pvQc*MOF)ezqcYWP^R{{ zKU}mp`7J(g@np;-F=uLy-pz+?tQLDI#hSNc|kYCKK$G)@$xxv<7Fbsx+Ddvg56R zeMtD_S#fP#NX5?1Z$Fmp3SHh!&;}O{n;TF&Z+d#IGOr@V`zv8utvsj`_VVAxWb6g^v zBeLaIc?G%1rzaVk){hiQp8Kmlq+G2xk*U(#1YcvZZEYj4XAxlXa3TU^?EN+^O>Wan zf|)(kZ*T;KerAr=+teYgZMQOPH(7>H0A-@h(?llrq-Q;bu{<5=ok@6(U&%`Zj2r{z zm{XedxTNkxt{saiwj^efE;>FVhCF?kjI^9lQimhOS7^1Nvp?219UAS)HLS3r>`JUa z)HtauupD(pBFGVy)^-EE(%e*zirWOg622{A8-blgz?*wj$J;wMH4_OpI^`-YwYub> zWKcyZ?YIAyP8@D@<&I2u%o~klhiTR32vq(LTkKSMSOZi=v6qh^_6uO;uy3Pstzu#6j{2s@qWfl01_<5dc9H zKINl6$LFT{JmiqYFVvYv=tguVgu;$0`cp;pV_(rvGHc)YLed>OmO%;#xgNMVh)Sz= z=2|sQlps)gF{hFAD%)c8>nmkzy-n~n7TeY~0*4rZy7|@ttZc|;e`hn3XksY=wI&io z3`?190^2P4B^ObVg>*7u2SshvX{%2fi6GJLkn&l#Ok-G=Cjefz>oySl6%_~SDbdrP z@TJArq}?Uq;%aG(ARvwGowlS)QTo!@cRc*Ar2<$WbxF_ATP6Xlm%Y?)W16(AGk(U- ztNG+k*C{*Aou+OR{B&Hl)NKTI7y)LD`c^)bxheakkesL+X5PXWCki7|o>!Pnfi8ck zFcXQACuFh+x^{K3=r;p_(}7Vdg?mQgOCquzZB^IP!%UFC2`K48hhHk9x9MZRgsU|H zlaR?Tn5$KB&yCWBWR9R$O8@-DCHkS#DDsc0ILVvU`xoMpPbFKj+^W@#S=L0*Q%{B} zu}eFwYdk|}qN?&-Yw62`V34*6em*uwEhi^=uN~ZAz3jEgb0r?*Sm%hz%NubqzEvU9 zk7ARlg9v$xBZJ07MZObOT!Y5#9D?9E8R$5fcQ^r{l-oMh3Z;?8uOzXyT8@i#w$X$R z?W&++lG7{GZ)?+1-7!XDM8g&=Hfn-ym?J;Ko4|z=w26w(s;hyn|5BDkY*VgplxXQi zo4mu&Ohud3*ETWdyNi}a|2&*p>(&y{38Uc{uObJ7GAiU84Ii@F4>_~vhZ@tphhD%Q zt+hS3eYgPW_nN(TxJ)18xT}k}kK-!840wS>87;FojF+6Qim4BVDokKrp-Wdujav0; zPv$`9Tt4ok&kXzG}R7EJmo z|LC)1RM{?$ITnw>w(5BBE(0!FCT$b%Ktuun9$>|T|9&E>kTiwF*j~Zcm~30y2pm%p zQ1ZiOS3;rZNG43id8M!P%VdCt(j)n9X`D4r2t+g`B(OL>^Sc z&Q4QTUn+5mMsBfLMHgDtXOF|8`i5;8CK~=9IIAa`^ktji7ec*dZ6k0rM}YfX=_!-d z*jZ&>5S7@O%bd(4)+UH9lPKLOEj#(yhK`9RNLp`7!(;L(CE7?^h15323DmCivh+J& z*7Z1!!ynM*(Axw`IcoFBUqtZfg+ls|iriYN+qFgR*srQVUN2kZd+1h0R;e~ORrJ&z z8ryQ2xCzV{7*ag6c_O#TmS;-FWSii-Y1nqQ5jeIYu#!`9(<1@wZ?BHg=cY$0C#%!!&l`yN;2H?4G4*pY2B>83A@xSI5(EtF53cAUn^=bpl) zR#B6YaSIG6l`%!bak3)Y+XUYQaogEO;Mj?PlgBuP(0^tT{r6y;7h+&lE7OUSpM?@_ zU;T-k&~wE!NlCwOhpKGn zt5_ld#g*lS4FYT1%JEc5DNzpsv|D`&z!Zo&hh{E&)H8l7n&X&@o$0NOL^g_ju}_Ih z+BU&wG`96N0!Lv4m_TNW_IPgeR*-GqoRDtA$|kj#0IhA=Y>NoPvK{ceuw`OmA;xhy z>VlWTjgzYhDwL!dhmI6>fRp%4Otxa^OkUJ(@hL{rq)7&RX~rOBMp(o)k+RW>ny66< zk7IT62D+H7T@uo#Dreh)v{Ka()mDo$R^YfizIr0*W$ag>MOVWuvD_y35}$2j8-Zgx z0!n8XLT-E}vb;v;$dX=1$(-jqDQu7q3=vZr$wevCIuuDOd}@OkhzYLBFj+&Yqcuu~ z35-6`(IQo8A(NDPGFUAuL0eF=){`Q|i$9shO(QBFO@TCYC5?kx*@2*&I>t$Z+SbWB zeIrhruBet+T(TkQV};r zpvKe@7rIb$+9sQbPAl=Q07T28fd!+4(i)PIHCu1W+G279f7ab0y2OTsO4ED7KQ1P z3nFPZvrECSLXWeA5D0M&PKIR)2sMc58Qb2XDPh{%Mv3>{!j^?M4?9{gBf+w+M)Ym> zoQc4$)=8TfrrfXDVT#++0xKbvkSIY1bI~-;c6^F~kmi(PsK!Ktj=uDSx`T>@D_PoM zBT7w#G~F;#U{tqBKUq$1w3#C^M`<3AL=`si@wg4Y5uC}mV1%xcC18j&#*=r%1Ct+G z;}0VmoYMRc4Bj59J~Fuzi8cA|m*^#6`WMbnqZHaJ0h2=bKg&uxM)QQJ1Q5!gmx zDgv1-l#w97G?EFYwA<_*$<0{4>MyBBicWY$GLEmx+g!es*ppZMqEC%-(%U){?AiFj z5XT{ZvPMsT_ypeMxa>QRl=NR{(jOa7i4f%H82IT-9wm8@`5m`Q+!0y9l6%mxo3sa8 zO76s$Hxbl~4arGwy`pa5qQR!PzGG$`otVtoCisZew%SJEn=Jxu+E^WXa-BrPR}wFI zNO_4;x*$c(^hjxG^2(LzCv@VXl@t~J6q$5IiNtCG5?D%w@Jux&mr0!}Q+(czkxzjs zA7PHNHd}35p9wcU%fKM=OJwwzQ2fPA+SCiGJP8oNx`9b-%HlqcdUP(S z5gYVVp4_%bGQ|sn29JiHDku(Z8`}tMBXG1tK*>ul zCysF63E!90yglXfgPDv2$Gs0ade?v(_mGHx9F!Az`oVBmhsRL?bA?J;L)>1$k7Bm% zZX>XbfRjp>J(!g`g1fw4cRrzj+iFFUks6X#t4kIs`ARmmmFmFABn+{VeA(VmQgh`gVPPY6d%Jdup;2lSBmiR_3@60IK^PYa(w$Uh|rpSa5 zj}si+F50JiItPW7NnJWd#*qNJJ;P zj_>>t$(_lH@Fs(YD#lP4SzVU)L9^cZPc zOR3eG?&dx7^e(Y&A0MsaE)JifImjPM(~01?0;DT_Bz^r{7{D}q9#Ho28)a_z4UlNO ziua2w`kmucgu$bB9w`HX68+>+XPe*^o$X;8fo%lF5pbd)wYIt}LRRE-N@VgQG038J zEJ;DynF`3(vp{5N8>3_@R$GT zcH*$+?J3ZNiEPhr&?Yu*9zY^LaNsN}L0RJ#j+CUU+dPzzF7DCeWRYg~A9q}7L&WX; zY=8vCkv6kw``ZMs;2gpsBYz00>r{3Jy*uq#+mkM8@l0}dPduF1^loWfyYqf`@!p;7 zW$oRKxh9v+3KJVk4md533*QHgiQSpbCepiq%R3sFArnc9E6P9J1uqbCqwPNkJ~u= z_Tl@#jqFCW%1N{ZB+^8&a*IL0 z3W*jw0H(~;&lz{R6BbDFNyZ~iAN8RsF!?SaPdF)i)>#LJZydl)90=zLiU}N9T#4K# z5r&2*g0oFyT=E}%Zc1Wo6MPetZF3ueZ3Olv0-3j!ca-N$VM;;e-b zj+vPE?{Tl5KLf!JYia*%Bz?Ubl41IVcQ^P4Q9YfLSMhx8v=keaPum1vqOxslBe0FY zUPmC)j&7|JJd<5xh%%WtMP`CR$!~dPzT(w9lO=LnDNJ_K2c^=}Y7r|9Ce!4$HWPz_ zq}yi5hCd0?CTG!#gVw__MNvm`mv_E8r~UyX?zB(!ggEYaemKipIZp0pF~KX@3rW!Q zO5YqtJkH*qSMxSWY(35yE^HHgGZowBHUirS>{SFZ$vN$2ugiU5iV}HCfh43aaYRQ; z2`v&NPZUgIEaKzfo=73(_Vh74NoCp!3?|tgEo?MEX}cj69lJ#kdz4AL$Yw?l0{I+w zzB1$Q$A_nU?K>XH8XvCmYW}QD@*z(MeJFbXCjzv21yA4dOIiDP8wm7mf@h?*WgCHQ z1okWfOk7!b#qHvx=eAS0DfNmvb!yEF2q_8*6tZaV4+FfGH%8b{0`0HxE0$Zh1ENB= z{5T^J(9Yb;TOTY@MTatt5UBNp#H4C5zvGBDDvgXu*Q7XYMTdMb%yn`$u6|BId}1@n z^D#_*o0hk4aO5MG@!@R#d;-Yea_ne+oHUc|~K%2x@HJNT)Qm#$( z$#u5tbKaH5d_~W=Leunmh9WFRIXn7s+)9+ zI1$&RA0sOI)~sKCs)&wDJxM4{bBucNb_;d+E0|HEeGqqXj>8wE4j_^H_6_8j`J?zg zzJGIPo8U7l=aRZk^Pvr>tjR-D-)e0mFc|?SP_m|1s8e95Hj6N&$u9{sg~hZ);>WMX zM20n<#1oT(PT#7e9ryKl1aeNi=-YEu&TcjNtp(G%GRI zcQ zQsR|96TRhlG4E&Wx3_3w3bxfY0^10j8zP`2iv*`z_sDpei1QYaZe_0wlxaK)bY0M3 zB2}LUCg-R_BDL~&Xhvb&cDE7OM&KBZK$j}n zQ6@x(E->mT_{I5hI06Qu4&0ex3;kuuzIF z9q@lh^F$IJvx+hBBt;v1mVTSy7lOKFZ6mOaz_AqpB|>K4nh{HzBwe+aA-Sicl4g^z zm6UyT%qwd`DsKvyEfy#F$oM9Wj?CXW5rEDUK)p(j6GK9c^cK6J)1FB9Q)E1Wv7C)Y zz7yFdc*kUW+(uv#?DljuBCSeVUwo@HvP`T{?Kd6$Odgv2145e--Yqq z-zkgv5QZA-YgV%^`dN{qlCfLAwfSa8wj8x}wy|et%WiaML63O`1C}vg4W1~gWRE)N zg-sYGO|pG;uiGCG;8Qt_f6jsHI5~(BkOFh!HHDBM@>xd6*;6ND+VBYlEn?7iQDVQ^ zL#yaggmQkRE&8&(2cO~F)*D2C3&;y{1K~puSxRPJzDuBo)3YVJTi#wrV0j%ZTgybd zEVUsQ3%bFt(jJi{AreCILrRkvO4uZPDw4>M)#rrE|0hX&8Puk0N ljg93ALm^2dLr*X0f8bV`CSPD0#fbM4h6vdcRu+26#@d8&r(cG zQA$jVMA6CK%+kgb0zxt(MIBy4c@!sCJ25_f2C6_3p$ma*E&)?CiiAY<4@M%oXap&` z%GuHHKx`x5pu>Nh`+&~Di|OC4ZS1$iHrpzQ#Sm~*{S53R6b-Gbyboi z_0+4_r)K}K&o2bjTO^svZ=LNZlv zt#;~UXcisPBbT3oR76J5;kZcGqB|;_+QrJos@*f~E;L!iJJXKEh&@jFXeOEn^eOM- z$dD4nah6}q2B}131C}d{R~b$`1#68Ju89!D8`yc35ahg#6eyPL zJn{=>Czv~BiciW{vC4^FKEY(9?935K#4SC$#ebQhmPDQdM`0oiNvS-pROZ@vC&h6m zBx9Y0Wz6Fen*{TZ2(tkcUX`T*EePiKV)wJ4N9h>?tPTmRC<%l-t(n93#NQIgwT}7d zuLubcdnhqTO1Fr@kb6RCbz_xSlIpiO)IUrDM$ixa0r4morjb8)NbMv3VwimYxSR?} zf#01BRo(o!coMlM7zmUticCQHWnzV@g(()^H|StQJxj#Z|K_OAkEsW3=sMKpaM9=K z-9W`p*@#7iS%bw3XekYkEE4+8gX1Fnrl`s|*F6nu2hFi@MY4`oA!eE#KmbX#j`D1{ z4LQ@mXR=b!y02;Yp8wW@=l#I0;%0)&D!oVrpLp&|SiYUVEm%Z0efRM6b2yVT+5TQ} zI_`aC%QO#dEu5I4ZfIGg3YIS&Oaij7ypmUG7ArpmTugaO6wWzDzaSqGVz#c8GF$Gs*7TE zTZZ<2(SYLPCRYCo=`BJ6fHW3n4~HK$V2u`pu5qSGhcgR!%Kw@ih;6{I3~2U0H{e=^ zNA>^Z@Fg3{yia@vt|q9%;m#${OK7;WP_8h&}<{w%uLZS;>I)@yp%%+ z1{4~2Y7q+tF_%30L%e?k&jjQLkGZkj1L?R46?#2A7zN?=Bkc#>cU?W$e6cSEU#^&7 z@cYvLN?wvW0Co{X``JZV3ZnlcIix$lb<1o7bA2aE5NV|N)$h)dkuH@jr6axmy^13D zTQgQiIP^En(fB>dYjW|V)*^2u7E5SLrem06#PY0Lc{$xl*-2Ff6$Wv+DzzfDl4gnX z)>Z5#Dl6hqEfec{G@i@}s%a7?stXdXM5d&UQDU0aFWQmB6uF5~848*TxG?4 z>gp|u4{F4vj7oW>-FiMf&pdbXcMmGS3g{HM9LbcQX+Pb6Dk#}2>(5%S6KIhzgrq8H z|ET<(QzDSx1k6JulK!%_9k3#Qz1z`VLH(RNbiV9#6F zTi`9*t)1@gnrcP~F69;#_dL5wyPn+G+$_D{n=V~WUBoA+eN`n6V;8;gZ0VJ|)OtVX!Qiqhv(R zYx2nK-Hfb(!;ESom-Q~4-i*^(pdn+$-p+=|j*)H4Y}4v}wttbV?>Ff8;!Zck(A+rd zhEt4bl4(y$#)3wrrrLs*6Q-=w-x&O@|NQ|jz9a$IIC)Fn+=64 zu*>_as;jyURI6Sq<}3E~_Rhkvb)117xKlll(M*HEZ82UQvy7jtlt?R&YHm3>D?}Qj~{dV5gYn^f3 z+L+%lh(Hc^m4Px`yI4D@mPxyo_2YlW8@O#;ZGQKcxADK`J#K$#d6xWA^VqzdyNkTi zI&q!K`U~6>Sb8aY@FGUb^Ui}7^n2ocGJE=Wb&hAtr^WnhA z^m>*(kuvcz=0@H|p$P#Sv*l3-QIGGBW0zTkSg3KzL&wCsJZAUl2LP+@I)lUEVF{=S zbD-h6qh%Ij^|&rvQy@ibMI790_R+%ZgRzaVld&&>1R*Y4jraQQazb5$k@n7!qw>!E zU{?fMq&|@!S0AHD>P&K31~*F`?Uk*P!(2sC>=pU&G?5?63bkhPdc3H#lJD{ z|0==;)&>=hLMO|p+uW_xAr*n#RIay8(c*(@u{lGs`Ew; zs&@r;;jfo2y1ygX zB-L9x&pRnO$A-HOYVI7AmnfGi*rstyM^XUW1lz2gUy3p2ay%tBi#kKSbz2j9zBMGh zeLJPUWTIyB;9_uG?K@rt4}qB`9n$MehLied`t{9r*gV;?%rd4kg({snt?!$8K-rBv za?T8gjM-}fOg5YUo|2z5Z~T~?XtY#se~HWm4u?=89WtCf)0~x_7{wS_UQLR3M>W$A z<3{1MFyb|t3?wp9ye1+QGZvc`=cQh!>OFeyO`rO`x6}J*o;tTdR+)mv62y_St(5MNuPAvw3V}G2Xqq&c9wo z+>NBbw&%Ly(p@2JZ8M7+O`T&PZjNl;T*Yawu9+*Tx8Za2>boD|!*p+a-MN!lkT1+B z<`cTWyf|3~c679jHB?x|UFGgCSTt3&yI%};*}eomaLjWr*i@R%r~ZR~F@4E<&wM_O zfyRK(MEwP<;xX|l+hKllkOC@k71s0!7IqhswiUdA(#hpwx?{rpoZoGie%=)BkKD#G z2+8wwUp_8jg;2rCOUvKqVCJ4!WzM-g+&+SrGDkRCawE8%|JfX^fElV%u?}CSfC3lJ z?;zM(q&r*z#zkGH9+yY=hb^?0UyX~p_nqrA2OEpag6(gQDgJb&HE{X@UNO$+J9lNL z=UkX}>+L*OW+M#m)wydcE&A&;tv6TyI=zCuC?1;rr3oN(;NI&W&7+E$7Fh=ak99&JbvTCD4Qy*GHKf!D94)Zy0?6~cdo4EOPV z{^W;P#s&!Y^Fxqr6PdDAEut$M(rxvRz(cvrmlg1Os3~XD?S}xSA+>Y%%c@^^9_y&5 zv$uxcjFI*#?Ea42>yHxrimjpczWQVn+@=~*W^!^6w4XKr0y4}J0_M|#{51yU^^`v2Mxq5n1NyzGDS4mx?M?;`W)dM9n!aLekv8Yb9zF*?gQ zF3qxQFMmBz?@2swUSq^*GV^;;v8<9wrB4V!!X0b01e^2+Bm9G&^|_sum}|Dx<|4a4 zS&-peHh?~!Eevb4-_kodJ#_|?nNMa?r0Ke4V3D09pvrnNc0U>!ji zv)khd{A%RCUpBrTqpJl3kKG0di+PHK%q;48*{+x*$(ek1`?qgviI)di z_U*GgG8KXf;SZmoN$z^sT+4uc_37|!Mi(YZ0~uvBSfozqvTEZn=u+R{PKzm~K_*z( z*q#s+tLg@^H(BL*uS%WnU9|7%WAt}=-mF)hmkPa=Po%RH?$ClI9#+hWHSN3ZXyvu( z5D?|6z@-ZA-(9zS9!5XjAB4=2%}s)Bnjns6FD>Ff-Y)$1hY9SHr+xl8JAp{UK)nIb zra7*Qvx%vlAM4K3JhnVI2_htFJsMgs)sKJ)9d=#4r$dMNr=DTykHa8ovCNHN;D$1%orFKD_f7tJ8jn>_y-VllD+$?m8@Qbkh|mWNy@x9T;C&B zPtV~bR--wFdczk66UAC%lijA~Dycx9x4WhNrJnbuEEcu3_hr?(y63+yw1@}|ih}7c zdLG-bTw?ssM}?-fUH8|s);n6%MzG6EdR}rhJ@3ETKHgt)&e0}q6xU< zyPqcQ+f$@u;Zk6b@6rhydFw1-EeYz>0`WRwX?jgE~y-I^8i>X3UlAs9g8R zqV{4mnZ`Nnvza9BpM;LLOnWpE7v2fL@0B&}V+=||h^2A)DfNpY`UY@MQ+yWyHpS;{ zNO-~-e&=t~800?y(&I%^Nje|i4;$`TU5KF5y(EQL?3N$CbjWp3-1lJOwrNre0ESc2i#ogQHu!?#Q@djHw9TA zMI3r5n=+n|dlX=i*V>))7(*=E06z?hhiu;yNjyv)x{%yz8%^@Gk z7xgM8?`@>HnLp8)n|>cYpCR!j|D21dTC;acwF2~rB31OCnP{BBX_O)j5Ia=K>-~B) z;&k4$)E+9d9gJ0^+u=qLHCXj$%lD-Xwy~-&_owU4Bv%^S6t~!-uKPx!r)KaDIkWqf z4VSB*%b0^JobsgdBGvtpFYrYs&n-(e$vdSugU`K&|FRdF)$9Ju01urSY1pDs+qOxS z8TBnJ4$b12UQ{86cVSEGcnk`6)xJ6;lWGwb2?s>{ehL4yap9|uQ!Z)*^Mk+S+P8^V zv?Kr&_sJ?Z;zs?J9cntSr6q#(->=hTR&_K-`Op3gWSjTxr|B~$Q7O1-)@8wj9bzf) z|Kwb?AkXX46&eDNXcv|Wev4D+>M6=rMHE{a)~lCE^MMJhF7`KjI%}A&uv3lS;yKJF z*S4t>Us-^Yz{!|3YwD%{gQJUUov8u&MZ$|PM1a%^0@`jQ7MNqkZO{wx^m-`7)aCV% zT2exp7ToE*%eG;#3g!7LtxB=`)pwF(iMxXuYFmo$OPW_aH33BTbxJKaAC{+a@JQHV zzd3xs=nW;PtQ-vlrBg;$AfLGK5lU&PyF{rOuJv!J)8p7kv(+U)X4;)tYoR4EhwtK? zALg}7tF7K5B!~UWYqafQj(ZKL7{jP|+K6@rib$aNxj5BLb+cB}2zI{XqFcMB`}u^r z9Lj1sS785d5SrURB?y!ZCm|$mutN!{Mf_v?rq@N+bxY41!wFePuK^DC!@6_My|%ou zLHyR|%ovZbauf^!tP35hJsG@T>$(?6oXs}J-Riq<4f z1}y1Y4L-V2J?;ON=>JMpBvwqCw6ZqI{l8d%B%9czgKj$Uer#q;HrG7W^A5ec$MC$8A3TP0FTd;7q%p z6JIUlmqZNNqvsaC%{x4j7FUf#=v_KMoHqGoIp*c|n4G}Av+nkEsg}Mv*xW#DB#No~ zh`Eq|?8?rxf|l4*Loyg?!~4vEJx$a#=NXp& z{tTc_UXPpMua1n1bFy4rnF9ITZ0<`BdJ-VbDk{AOL?$ul3bDY>Hu+lq+!}_{yTVxE zv9{Gh?^kg-bn24A7KCo9q=clqeEp-OSn0@sT<^z!Av>Z>1j_<5ZY7dnu04W;wg+z_rL9%>Z*prZO0G0DXAzX3fY&yOG&4myS)n=RTyfLY# zYYv-mWx3B2v8dG8nTep&dQ$)6SzTh&g)=uIHgA|SYwAPJ%iu@*-J*$fu8VALoFy%E zkUbhPm)jjQZpU@^XJY4a_h3LFP~FP7QQI*oR?sCl(U!R=k!z%%f4PegP^-}R)6eyV z@R6hkq>2N5Df(}QmsBVjp1tqF_o2h?hq{$wA+p0FlUJG1|CFsyr!>=+J&wi)kB zySe~gO0w*nf(&`~EgWA}zQlcDx4G{wb%!wp7u3}&)BdPJN}X`O7A9n$Q^o!&yZrO< z6V=X5B97T=?}*eZ2)#9)nvVe8v$I|1q~xQP_HfUMSHMu7so(>Um6l$HfT=Dn*px(_ z-<*Et!Qn=0gLECN?#>`cgO=J z2rBrAYos%VO+WyE)0Ha;-b5)n7`J(EXXe+Rnfpi5PflnknEnSV=w-)PGf-ZS1;nhL1C!37KP9#&_1)(NIr^S!%iEb?aTlAbZwt7lteG zjEW7HQA=aI<7wO&Z9Mm3c()=Vl;5vLh%ZVWqSuQ7Fq4U}wD!AFX<`FjzvZ_M`5wfA zPj~BjgWuS`l$qP$V@oSf3FKnOU^8eV#lup@ro!pyiNa&DVGq%CYQ@l~Fu`s0e6F+E z=AuF{%MGfFlbK3pw9~x4@_-ga>0?wHqZ53+j^M$>o?@!D>FZft1l(Hvb0AAiK)U}Q z<3qgO>sY>jfXvFH7EciRooQd<3R+(tv0{zV9wzL2kc!wF4?4$^HiuZ1MXGFzDC8Py z6d65PaAUV3zFHNFMuQc|2q8+>JI>Wf$SSl1wP+w$OrRm4+&x-Dvq?e0aqf7wW~>hD zXUiE?j=~Wi{^sW@!hRmzC{zx{Ih{kOHw}G1;K0}177sc+;$}Buh{pw%+p}UB`f=C( zDX4C%g$11#gb_JyB57qS;j0|sy$UcW_z~mH$*SHZ((iECrk+J={X4q=htv>Hcqv?Z zc3s~UI1Md3?-d^_AQllM%_<767gqB~0+ppC8*YzZokME`<&Pq-d!ba^upo*Ev|ADP zi#H1M9)Qv=%@j&|FQG#m6W?hqG4=ifKhRTiCr$vPT4! z%#w&H`vNjh-~oDSy?EkcFo>SP|CG6|4ZqzhXWRIYLaJ3ZL0HxmK-QN|p<{?n-HUK? z|NWWo`mIq7N4J+m#yeSE+E{#M_K_yb{1OR?{@axyj_~FR`P!eT0-ge0GnU)wl{RN{ zE=4C+j0SWEvKe^~-2C&{=TP#E(59^C zAw`Y727|~!zf8u>vW`oD&r&0cIs1jM&$OPr_Og&SIMY(=jl-ZOdz7TR&z&{h4&>x& z-hb3NYcAS`5^B7<&wQ>%3&ct>lVg__h6$;zh^wjQ5_X!*W;hNn=Pi}cIMY!y$X=em zy8Lfh*uEBp24XoW%L+k5(=yr-DVxF8iDY#1eWJrA{in{WL>krRxU9n_v+$Hy%sDtu z4M51bLkgTt*5iOEsOpEBET`iVu9{sC`c^97xMWe=^6yC$EMlacZVRD?3qIQ>cMTn$5rMeo42*9ILNf|cm#KFEIm zs?<)#e+v-%y*eIt@1+pJ{WP;F@JHSzd-yDR^wZE4a2I&}?fr`b4#rbiNy$i=RP?pp z(VI+%S~kMha)kd<5^j-@0*9X}KOyt_mE$Q3PK!r>Yh3ku<{qk;-Ad<|AW7`rPVgL9F&Sj;La zZ$7L~?Iq`#luB#!b?Q{*{>&}@Y|~Y70sg16AjF!cglwlcBha~hZE@_tU-7S}BRm*H zq%?$lA4L4xYk6d;BzJh22ONTq77aKw6P7_61_{~{AvP+1E~Dg7F9X?b7hMq4}OTQ%ou67gUpmq`2Ou~X~xEe+1h>RUtc?yM9ul|?`&hXNb}%7Z3%-zz%e z-_QmjJt#}$21iS0Uetp?MFThg62e5>k~kJzprqJo{5%3r5ON~~<3zsEojHcJ!zY=~ z`xn;spla!v(jMpD_u{;jLa@~>tCw*#AssAFuw!3QEq6&jVR0$809WS%>;+Z>r_zE2kO)B;U$H+oo~ygQX>Dswy*=>RBUzOABb6E-C@ zt2B*bbpVlwEbEJ$e=|6R0A5}2l|rZ+G6{hZ@(67ueAqx8Fisf(Cnc{DwJ=2HQ_y2P zYhXDnOzQI>lxS@Req%Z3(^WLOY*~=yegMt-g0zP!0@5WN_<5L1GofPn$)vT zzILT!G|xOThZF9Y)ngcnH8Ca5=Cui*cZ~Ejhq~16SHP`2s+QEaET`R>J?L)W>0;c~ zUN`ydzv1Uc5)(MdZVly=})HhZn0;&K~NNI9gQer)x0@$ zAY7k0hdGS@&^Lw|sitSKvARZd9{f@q52EmlO21SWoqxvCfa*xhJb_&;|3+ z^4m5qhB;*nY&il z8u5*r2>k;0=gy2L7gAq2W;ua$LMjD$YCedS#HJdhwHBC~;~UHI)MXZ~ip4HmV)zIY zmVpJ_ezI`4%6)o#pIw422RBkc!h)^arLe`ZKU|<%lw8=B*d~Gt^;{`zN&u>DFvh%lQ zeomNx83|<7XdoqlmI0u~5?#Q-^vUV8lokgy%-rN(#6R0OGyIMdYW7Qc9TzABySEf-qw?=@1RXf$YTqr=>+) zZ_NU_H>?nGYiKje{pW_TYklB@Rj5W&D2z^zVge-x3SmhAJ15vva9L@T0GF;=cSkz| zCAn`@C_1rFA`X?;pIo*|XAfhBF|$}nm(ji2 zrv$4PN#8DHGkoB$AB@yN$PX0@7)R>`s7Q5IW`Gk|JYy|{?d{V^gEXMWo4tf4HMkYYGg_RmPApS zMCCv3G}`JD9>;CFG=pMaU4Dk*9{K`Zje6^Div?$y?yB-)qm|*gRf|VpjZE!LIgZlm z{81+kyWc_P(D*7e44uh7X#uqJ@7?t5 zed#oPBXq8|s!LkU+UD*&mb(r6MKM#-dgfiOqf6D7qN4UcAk1-UL?PR(M=j-%AGL%G z^Y499MCe1gpfwj=a4PKYiCE>dtqK;KR8Mr-2Jug*RnSRAIwCnpuDtyR;n0ZA*g5>r zpmtp8U1;T?i`WdE2dQ%@L}4OA{HwZ>yfH)>6x{)|S$T2O?0Tnz^kngzKU9*Zk7{5` zWLI}^gHlqt?nE(%~n;E*TKS!|Q;qZsZ3Bqkc67=4oZRFev< zax2SKy;gn?+&kH9fj6?$X~a-Q(>biSku!QMR*nraA7GEKWU47WkVOYi_WP}?kc&Qn z$eSEn(9lWfaJKF@nGpqhYN@T-;tLLA#9mu~m^bIlj73Oxe^J7BPLv{tF`Cj=EZ-vw z00O+l4CyC&iz2g#Z6&S)1Du8qPM|Z%?Wk8!VarK%wDPg6wWuil(}%~{BOB|4pYRiA z5PFgLEIEY_STMh9*CY#Dl%}VOn155ql>*N-+#$coX{`smLP_(Yu9leXnmQDoeXX0L zv{{}x8G#s&rlx~G=2f}LX+D`-7Q4UjOm?o``I@F!R%=hu`A|_;@Np7-L@Ois_TdDwDzzU{3*^1K}@_$9tQYzbwFkM>-x8Vtw+piAx^ zlgV)nLg0Ud`_sT=_ernW6sp4Dk?B|G8HE}uR)6Pgs#n{8%VdEyz?wteaM98!;U3br z2GL8hLmaw0d2j#|T%v2Vf)a=(ajNkO`bBbv#gYzSuB&uoxR=w>jTnUGS5*Mp5eclp zRmFGvhExk)ScBqOaZV`yXEJdU@|w2(sc_GiB4YM-(T+Ht5~M`x&!IH92F6PaLW z#|;0d^n|%oTX1CHevb<&$1RDG4p${ETU%_w+MJgb=KT8F0Nw3})95~B5`(fS$f!PH z8&7+Vg*aj4ft|=T!Xi0r5-te8nPQdtH;S{WSS;2k7f{e%8{zfI9%b477+aAO!VEKF zZK4#f%+^E9am#+tZWQ-L{IsB&4}<0tg;B2{7aQ}z$|75`Az`pHu-GXO?wYL;-G6@k zD?+42Qe9FoyJ8fj=Rzb$FgF&|Dve^0&iMW~BJ`ta-{#$5$e?uP+S~ij_nXiUxAx@!c=40em9Ylwz~hHK@*_VWA@ zY6LU#SI3G3X(8;@I9RT2*{$giVZV*?U-kOw(szbLR!ja^_T4YEB`gKK?C+mDPYvYX zU?B7Ql^_H6^07gzih*bHt_U?nkLk~#M&PzJx! z^g_l`lV`mT@qvS&iKd7?)1)* zUqO3--tM`=@;Db;*{NOIlt&j%H-(dyGv*X=8Jw}br_rAo&QzdNsm!YgCTm&kA-E2y zFxw)rfop~d5N@fHW0~SnTOS6V*#^X`MxihxUhC|HX8wMT`LasW;lWCVl#_$}^U5)K zW}p~>AsmFW+|9QwMFR*^MSwtD-1O-Y>946A$wLMFXNoLE3u`~bEQJ={+RqsT@g2Gz zCC+#kg8BPe(wWQoF=%tO0vfIo$9&Ct^kXzs zDR&4s`Lp3hM||~5g9CIV3l=R2D&VSe=GS`9t4@pgRr>yE2+=`7v*CvR8fxOQm%+AO zn5^=njvSxcdN=($ixY-5*Hs@_ef2-XVu9z&tE-ULv1iU|*3EJP!|$s)1@pMF5L-`E z?t~G=b{!#l)v|l3E12hMmi`*YTr5J?Uzp&`rgHx3K%bz^8k3u$A7o7PUGSUtBQ>cP zyH-&8PNLJLly1YL;P1)j7}ZIWi};&-IhZGs&ox)aF2qvS_+f&Na}IF&5mhk2tX25- zS6*4(pM6^3L8nyLG1XMfx%g(p+d885T&Ev0^H$3r{g}PZ|F7j^JnAQy7!Iv+r3BPx z(h!>d7I0iAwwGMs1zrKVy&X{7Rv4n1&it+eFTFEw?ce#L0$8#*a#GBrU`@p6pp8&a zlAV(t)C7di9kcr>RPVlni6$61KX|_Gfv)BZE@fH!1_CEX?fV z_a@8vXA)m9=LHOo2^YEIuy3N*Vdgn&H1(3HgbO!59=})wXHWY7kY2NNl_{k;Yoy^w0gCPn z%zvIz^cm#O1oxf(cvQXD-4<;1e)m$_0eAoRC9ykL)EStUhum~Y*;XLo*G`bY5~Kaa zbk>zxJSpq;RrZM+O9CW3zLzj}3{W8a1{TS9a~n%xwjZ^WX^`eCr{F8>WslElNCYo=g7CHap4+bJK`2 zNp(QU?(>C7?XyaaJYQw zKB=h8meU4b{$y6vXzY!yqzGcOv)Z@Z)@*BbcW0L;e3=ngTg(()nh{HfUyzjJ1dz zNy}XD^(Dr_s}ICpiyP@|I#}Fk;E?71cw*?g&0M+eR%D>hIZFE+;{6$)9SuVTp5S0> zB!{OEj3rI=eIYbL&fxII{}Y}-w(305C@R=!{z)X@Qga}0rEPM>I0+~fd~O}Cc~7^e zh7h%_SuyTZE04ykbyqoD;(dYc2|-JIdffCK>kxwUbAeABOFFwc)xG?j;fY+VW5Qz* zujyDC=!m1X?L^D!#?~+f)ePAsH?!T!?*Z^cM><0uK1w!Oj?`$Y2=8Soo1&hoUbSWs zDezDRFd>$YpTe7C!DSp71Q}%Hb!lb~wu<^k*a()@n|?Mmg;~*ASr$AJ3)0CX)Ht|P zUG=&B+4SgJ0PU4%fH;w!0w$tMyb={wf{>P>WGfBv3(k8-fiRVkwPl%IktuVO<#+*q zlh>KnKoE2TvK|DzWd5;OATqPwrH}3rGCE%n6dShNlY{9!CixFu%qVV)8bKy~s<8s1 zVw^ot9_N+o^>G_3M8hP{_kY<V;_7CTgjHA+FGTIl2eS5-YOVD zF5TU+cca$zd>r5G3`Xr}H>XzV9H&=Z&V5H07r%GN+f**V;P`5|>}&QCrqP`<^2#n-S)S6eI_Boscf8*_2$j8U$^nxLy&Snft@4sFHodqu+&oWt+Kf;*(_$q z*-^!(P7{ej%On-7uuy>+D@rQNxXwXigA>FLJ-(LN?z_2+*cbOwlwe4XLPGtJN!(C= zEP6goe!4&~3vxM1cx-F7m-LoSf2noZd+3h4jANsKoWyNt@zM-EBsI0q9Z!95re$8 nrX0%WQ~bYS{{J7efDd@61ZU!y=li|Rb$t>qTN<&R6+BBtMa61jtZzjb zmniQwMtaIudE2XmG6>)d9B|hD9=K3sAcjiE-5-SknD`+*F;*C)d)SR0j5-w+O|Z9( z1J1$1Tovu_Cy)G%k;nT5P^77-)V1&dNVE?I2S8ywy|EhNn@w%v0B?5B3j8oQBmnQ{iw#o6Yl#1;SCumVy{sS(_!ENj z(GdU3DF=(o06qUe3;-?<2BE=VC_ot@4}mKw!NGC>C>RP>0K*g@P!I&J3RY5uCCW0g3y890XT1eEa10BB+5S+ry)-9^sf;70xT^4qZk|XccLgMQ@|qw6d>|o z1wX&vas7!7!dYSd+l_xk2ib%LU=*w{LH@ykXv%qbNcGP!AMH2?A3ADJiR-9anqI8eX}y|CG7g>@-5qgy;uJlZ zP<=J{*2p`oBf-Z-$5-d+JH7Ucz(jUx>hw5C<<~Vih+p~P%bo0=OMM?&lwX^)wuvrz z^!4T_F6W&dv~K2Y=5g?&wE^sd%;G#}qP5AA?2P*6)X_u}06URHOU;o%*@H0Aic3Xn z15lide@^}lRLZsJz{ePqwq>5@;NrM(Arp@1WV#MSQ#Vyq~nHze4{p188U_LvQuXDAILAGH`vy1-Kk8|CJSq$>ZWajbAyT( zL}e}o&@eA^7nAP@pP@F&lqgzw7ubIoD|XqIlSq=0T&hr8c3%5VYzxgaE&u~z^d98J zWrwurwrdlDhc+&DM^lq_l~&Xoiiu?P-37$?K`-x;+Fpn&J1n30Jh5ltKxci}=B1X{ zi0VXTahDIQ(usoQxWC&q@`5CxzHuScS-~TUALx8UUOZ*oSHoF-`z~gSmAmCsAauJfa<*ue`B#iP6n0$c#8xKpFux+_nuEz930oh%GotMs7e7^2r~*=%Mm zy)j5{R8e9xATDVRheThj)wvriW_8Uc0)gM%X_U~Z#8RNfy0bx%MM_TlB|-PLldp{3 zw&7I7+eFuabQtpo1i{BwJ%z8|jIPYnCaQa5(uy(EMdeJ)XI(dBm}tG6r_%f6IQR(+S35zeJxOWs*$)9IQzb6IWY zrIIx&-oJ~>Z+~+Je_G=Shh>C|A@N*XOgR^MyHD541Fng8cNdpSXeSW`RIA%_l#kiT zXtp0~9M-3~>s^IavZ?UX#0NA;2hQ5vkGHjgGS%#pU*8i3+9JL4s}hueG~YdBVi1%rMb~ihh%d_-<(8HP`U(b6Ib^7)dn-`2DQ6=arGOjapCoILE z`tY6PE4?|0TT5rwHd`Ni!{2(HZ!&E*H1;7~#_HU*EsL2?lG!M-;>dkex@*dM=WI5z zTQM;_l3i_umu2iK`IMvMJyYrCo%G0NeEZzAaE9j-V!9nyyg%vXo*)aGP~LL?fpo@B zfk|9u2z!vmQjT=?+Kv3eS68-F&RWd`9wl(^?}X+L9As;c8^P%U+#K!}VN)M3GE0~Q zx5wkC*o;8(j;eP)9kYnm$*m3h@&%#`WAC8OII&BJ`HM=UBd&yh~Q5+w2{Au06s9(E zUpSYfscd{OEzBJK;uqtEUrv9#3Q#BB-k{}Y8fa0ZnhibrgK#*4cfSG)#Ya^ z^RAvdv$z!PbAyA?Ql{1FRE0MGYNNKs)6NbfgS4XiGDx1xv{r{0q;-t9@W+wn`uT=s z5SK~op!W4@rG`2|CdcSSzh^+uP%KIKdMG_npoKtw{dC2T;WRlPV8PGYx7|s$EU~5A zygI|~#j?hX7CE^F?i!83d)#}bMaYLgKDbK{6 z8iP;oH`WMR_=gLiQ(-tX+ia|pSVO&yRMp%cy0>^FEToIs^PEZHz03Lpd=AB5*shMh z0=@Ns8wItX{jf<7x#-5P5OsDndg0-4;~Us5IpRUzvzf1_-3&8RjXK{3=buX)4E)sK zkAww#qQ5GpzuzYU#Wo7l6#F64;dRr)hm~5x5da%VyD#OAd0V8~>Gv!Zggy0aK7Dnj zZ%Y@u=}2x%LN?yTsRnQHf8a=%x_$S>!urZph`f+k2@BWTA@=au5m87-^Y`NX9Fb?u zhHjCJty8p>4_Hs67`*bYtH&+gK2eFkRk(%hSiZTt#shO&zGa7v>QuY(ee3%piTJ;(np0i3;(TaZ>nL8fKpu`A>%ogNp-1DsLW;$eK% zDg6A4Y{zQP=ICinzT6@q^ixX8C-VjPC?euhhl^A%s8y!q>B7vw$Div(mFaN0*t@As zWEbB@J)dDg_x8J`ddht zAnH~>`hd5b5q9lX+wvLXG1J>$V%88_U5{PQ5l>P_5+#Va({I63;wKC@jd>fyR;=~N z@}A=|eqXEn4AjouY0ByEm^5CkpB`>c^G3?+uea1iOO%Zpnri zqVfq}yqc7s+K$OBXs?nP%22Y7<;!h*1aP2K#OFZlmWfs=TPXensr7!?G@*@e=-Nc8 zQYyORV2XcCjxL6$KoRJ{p}*v+JY^i4QO)0h&Dc+PUSRjl&udQ717DSIDcxPNW@^C` zBONaKX5{w$jM-|l?i%RkQe*6T8>!tkW7T1-(KL>;i=QLr@qB^(XRg|cfTjmja6>j$ zw=TB8fyBhSTVcjAMlks55%;1WL)rUdq_lv(LcNK3il7Uq4@OkGb=hX(*|4u3Duro6 zVrd)DhqEC5+Um~%P&0dC zc`e)cYdCY_M5{==+Mv$eFD0MtrDUGAm0d^`GAH&tp4|Uq;v0PEb1fquiG0I#dh(34 zl)dK&oIypsZrJh7mGU2S@7l~gR+eUF2XIR#CR5*+V;shk1gI=isoq?Q-vvJ%QR)dF zk98eqV{)Lc$oCRsv2CLNlw4y53-oHBUud9mbWhj6-I`1-J$p98r)&XccaH1PO~HQY zY!c%aYXRZ&z@}{1Y+qFTcEAI_tZg(`hD0s($i$`jLHSwwZ?|9A?F-P?-0jC-VMm1Uk{SO>HMPM!Cd%q zuefj2E^8Umx#2t09tmDDeKUGl&+s*)XWi7EZhJv6pJ>SL&}idS49VYZ^3wU&MITZj zDoP~sv|Gou#f$OuYii{VCxqN|sf|dMu^5AYQ`$;!iQvE z607b=T=KvsHa+JGKlgq|11z?4(?m?+pfzVA%KONqMo2O7`T5b1TqX|1c1y`Fodf4v zC5!!)b|Kv*6R+J`w?)Nh8;2}kBrMswBPT<_ZuL&%K3>bks>zEup02D1d6^rWzULE= zD#(hr0BSQDR1CJtD6s=paNP3C&bWP8S6D>AJ~zmscyHW1zG-sCm$U0tSE;X@yp#k7 zFID}Xh&Of_zLHtS$e`U>w{;NU_XM}Pe2I6pziOXBUJeNT#2thN-l}DH1baxhXPyQ3 zy1@jShWcWr&xm`ZE)91He|G2Z?zeY9{Mf8R;K?F;AC%f&$70cfI(}U0>orND_K0DR zf{TmAnX;=qE*}r}d$$8yn>J~LSvP7Lr|%3Z z%6NXwaKbrQF@U@#Gh(PB9pHe@3A&xX(KG+@TD+!MzD6>MmCr)PPVIVc!R!!+1_JtKUb4dINd zOL^w5q-!W6Bu{?n(cF#DCsXax4fSXMgnIKD)1A9opoz+2akS5h4tyd+J(d12 zrM8O?+Nt;*&ZzeqxHbE;UCADe*k{Dx+#5Ctm|>0dnrxT+xuE@!vX*7mgW?~p)xmaE z{+g!ss=N$Gi4P*IR69*B<0Is`2_U{wg3N|d57!I8_O~IO3)@k=?7RKP21Eq;hq7~3 zJ@J-?#a55V*Rd@(_PzK>O#!pFhc}gZP|Lw77O*Vk_y@QuH-yO5e3FcIUolI!I-i;@ z&wx_JiaoY z=mhM|M|J({u(v;SB@{;>%W}INopOamPGk@Ci-iCfgO-+jt z#*lUQ7Jm67P!{ym5PfhqF_a#_b|mnFu#;dWu6X>orbERwyir*o&PbPcF6cG0NT_eX zR60q9KC})m?hW_oNp5<5kYGvBmt))k>1f_ZP%v=Iexei8Xf|{J#sA5Y|(G$FJWx zKTf#NU~iHAR;`m}WA2Z{9Ax@z0#oT}r0~Jwy)7oW`s2F&H>6$0M}Rzw2k313*ChIS zDzp}gAZC3MX2yFWEq>o&F+4jR=h$!TwSzEsOM}?P^oBu=a~eftzA_hda9R_~UwjPu zjNYjq56X`^y)DA>J#Qc=pSZGJ`0*PpW4;Y_Ym3Vs@66ukV{Jew6KYHT8p`k^VMUH@ zVaf~KAUedp#y)f<&MHuoGtef^H|9d=?~4dM6W$1yk-qPr`DE(L<1%f24)UG(uY-=; z;Jdr~rOY3QGMFN)@+DgP3jQpVxwMrjv8N4M*I%zZz}--}m_EZgOv@;7k0fm2TCvEP zp;mq>JR~G`z6_w)ohK6&P7gKa6t5MNk@T1|E+28-JPr9l(^T-dQu+=a^^<+)UQP~a z*3YK*b6&8U0ZHGM_rHlikku zf)<+(wBjClD#6=0$6j91&^vrJ+0(|aX%huhi*U4pvC20`*TS+Yv$e_Q?9%=@SHzb) z3_UB9wAQBiq#?F7WryE%Q?H9mRu;d43EG09i8=atP8tiSuqWTcjnWsN>zS8P1Az6i zpw$<}ufnfZm@?`2aKiZJO zsaaDvwU9#H5u@LvT-?iOV}rkVx<3iK|Ba{nlhvb;dH;aoqGN Date: Mon, 14 Apr 2025 10:35:14 +0800 Subject: [PATCH 03/17] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=20(#774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update:添加设计公司致谢 * update:添加设计公司致谢 * update:优化文档 * update:优化文档 --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fee67ddd..ef3431e9 100644 --- a/README.md +++ b/README.md @@ -174,15 +174,12 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ ## 产品生态 👬 小智是一个生态,当你使用这个产品时,也可以看看其他在这个生态圈的优秀项目 -- [小智安卓客户端](https://github.com/TOM88812/xiaozhi-android-client) +| 项目名称 | 项目地址 | 项目描述 | +|:---------------------|:--------|:--------| +| 小智安卓客户端 | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | 一个基于xiaozhi-server的Android、IOS语音对话应用,支持实时语音交互和文字对话。
现在是flutter版本,打通IOS、Android端。 | +| 小智电脑客户端 | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | 该项目提供了一个基于 Python 实现的小白 AI 客户端,使得在不具备实体硬件条件的情况下,
依然能够体过代码体验小智 AI 的功能。 | +| 小智Java服务端 | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | 小智开源后端服务 Java 版本是一个基于 Java 的开源项目。
它包括前后端的服务,旨在为用户提供一个完整的后端服务解决方案。 | - 一个基于xiaozhi-server的Android、IOS语音对话应用,支持实时语音交互和文字对话。现在是flutter版本,打通IOS、Android端。 -- [小智电脑客户端](https://github.com/Huang-junsen/py-xiaozhi) - - 该项目提供了一个基于 Python 实现的小白 AI 客户端,使得在不具备实体硬件条件的情况下,依然能够体过代码体验小智 AI 的功能。主要功能包括 AI 语音交互、视觉多模态识别、IoT 设备集成、联网音乐播放、语音唤醒、自动对话模式、图形化界面、命令行模式、跨平台支持、音量控制、会话管理、加密音频传输、自动验证码处理等。 -- [小智Java服务端](https://github.com/joey-zhou/xiaozhi-esp32-server-java) - - 小智开源后端服务 Java 版本是一个基于 Java 的开源项目,它包括前后端的服务,旨在为用户提供一个完整的后端服务解决方案。 --- ## 功能清单 ✨ From 2bab917b3e0ec5b4bb5c9e2351041447f8da445c Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 14 Apr 2025 11:31:52 +0800 Subject: [PATCH 04/17] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3=20(#775)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update:添加设计公司致谢 * update:添加设计公司致谢 * update:优化文档 * update:优化文档 * update:优化文档 * update:优化文档 --- README.md | 33 +++++++++++++++++---------------- docs/images/logo_xuanfeng.png | Bin 0 -> 20417 bytes 2 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 docs/images/logo_xuanfeng.png diff --git a/README.md b/README.md index ef3431e9..e8958e61 100644 --- a/README.md +++ b/README.md @@ -169,17 +169,6 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ 如遇到问题或产品建议反馈[点这里](docs/FAQ.md)。 ---- - -## 产品生态 👬 -小智是一个生态,当你使用这个产品时,也可以看看其他在这个生态圈的优秀项目 - -| 项目名称 | 项目地址 | 项目描述 | -|:---------------------|:--------|:--------| -| 小智安卓客户端 | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | 一个基于xiaozhi-server的Android、IOS语音对话应用,支持实时语音交互和文字对话。
现在是flutter版本,打通IOS、Android端。 | -| 小智电脑客户端 | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | 该项目提供了一个基于 Python 实现的小白 AI 客户端,使得在不具备实体硬件条件的情况下,
依然能够体过代码体验小智 AI 的功能。 | -| 小智Java服务端 | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | 小智开源后端服务 Java 版本是一个基于 Java 的开源项目。
它包括前后端的服务,旨在为用户提供一个完整的后端服务解决方案。 | - --- ## 功能清单 ✨ @@ -197,7 +186,6 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | IOT功能 | 支持管理注册设备IOT功能,支持基于对话上下文语境下的智能物联网控制 | | 智控台 | 提供Web管理界面,支持智能体管理、用户管理、系统配置等功能,方便管理员和用户进行管理 | - ### 正在开发 🚧 想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3) @@ -206,6 +194,17 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ --- +## 产品生态 👬 +小智是一个生态,当你使用这个产品时,也可以看看其他在这个生态圈的优秀项目 + +| 项目名称 | 项目地址 | 项目描述 | +|:---------------------|:--------|:--------| +| 小智安卓客户端 | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | 一个基于xiaozhi-server的Android、IOS语音对话应用,支持实时语音交互和文字对话。
现在是flutter版本,打通IOS、Android端。 | +| 小智电脑客户端 | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | 该项目提供了一个基于 Python 实现的小白 AI 客户端,使得在不具备实体硬件条件的情况下,
依然能够体过代码体验小智 AI 的功能。 | +| 小智Java服务端 | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | 小智开源后端服务 Java 版本是一个基于 Java 的开源项目。
它包括前后端的服务,旨在为用户提供一个完整的后端服务解决方案。 | + +--- + ## 本项目支持的平台/组件列表 📋 ### LLM 语言模型 @@ -270,10 +269,12 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | Logo | 项目/公司 | 说明 | |:---:|:---:|:---| -| | [百聆语音对话机器人](https://github.com/wwbin2017/bailing) | 本项目受[百聆语音对话机器人](https://github.com/wwbin2017/bailing)启发,并在其基础上实现 | -| | [十方融海](https://www.tenclass.com/) | 感谢[十方融海](https://www.tenclass.com/)对小智通讯协议提供的详尽文档支持 | -| | [汇远设计](http://ui.kwd988.net/) | 感谢[汇远设计](http://ui.kwd988.net/)为本项目提供专业视觉解决方案,用其服务超千家企业的设计实战经验,赋能本项目产品用户体验 | -| | [西安勤人信息科技](https://www.029app.com/) | 感谢[西安勤人信息科技](https://www.029app.com/)深化本项目视觉体系,确保整体设计风格在多场景应用中的一致性和扩展性 | +| | [百聆语音对话机器人](https://github.com/wwbin2017/bailing) | 本项目受[百聆语音对话机器人](https://github.com/wwbin2017/bailing)启发,并在其基础上实现 | +| | [十方融海](https://www.tenclass.com/) | 感谢[十方融海](https://www.tenclass.com/)为小智生态制定了标准的通讯协议、多设备兼容性方案及高并发场景实践示范;为本项目提供了全链路技术文档支持 | +| | [玄凤科技](https://github.com/Eric0308) | 感谢[玄凤科技](https://github.com/Eric0308)贡献函数调用框架、MCP通信协议及插件化调用机制的实现代码,通过标准化的指令调度体系与动态扩展能力,显著提升了前端设备(IoT)的交互效率和功能延展性 | +| | [汇远设计](http://ui.kwd988.net/) | 感谢[汇远设计](http://ui.kwd988.net/)为本项目提供专业视觉解决方案,用其服务超千家企业的设计实战经验,赋能本项目产品用户体验 | +| | [西安勤人信息科技](https://www.029app.com/) | 感谢[西安勤人信息科技](https://www.029app.com/)深化本项目视觉体系,确保整体设计风格在多场景应用中的一致性和扩展性 | +
diff --git a/docs/images/logo_xuanfeng.png b/docs/images/logo_xuanfeng.png new file mode 100644 index 0000000000000000000000000000000000000000..552ee78a2176a19b50c86803569e850ea192241c GIT binary patch literal 20417 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 Date: Mon, 14 Apr 2025 14:00:31 +0800 Subject: [PATCH 05/17] Update Deployment_all.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新部分的安装配置文档,后续需要修改 docker-compose.yaml --- docs/Deployment_all.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index e4ac4ff9..49804a57 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -44,6 +44,8 @@ xiaozhi-server 在页面的右侧找到名称为`RAW`按钮,在`RAW`按钮的旁边,找到下载的图标,点击下载按钮,下载`docker-compose_all.yml`文件。 把文件下载到你的 `xiaozhi-server`中。 +或者直接执行 `wget https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml` 下载。 + 下载完后,回到本教程继续往下。 ##### 1.3.2 下载 config_from_api.yaml @@ -53,6 +55,8 @@ xiaozhi-server 在页面的右侧找到名称为`RAW`按钮,在`RAW`按钮的旁边,找到下载的图标,点击下载按钮,下载`config_from_api.yaml`文件。 把文件下载到你的 `xiaozhi-server`下面的`data`文件夹中,然后把`config_from_api.yaml`文件重命名为`.config.yaml`。 +或者直接执行 `wget https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml` 下载保存。 + 下载完配置文件后,我们确认一下整个`xiaozhi-server`里面的文件如下所示: ``` From 0c855d4ba88c90aa4907275e219a21b9f9fcf085 Mon Sep 17 00:00:00 2001 From: expoli <31023767+expoli@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:06:30 +0800 Subject: [PATCH 06/17] Update docker-compose_all.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 直接提供全量的部署配置,包括 mysql 与 Redis 实现真正意义上的一键配置。 - 将 MySQL 的数据内容本地挂载存储,需要备份迁移的时候,直接打包复制文件夹即可。MySQL 容器删除的时候也不会出现数据丢失的问题。 --- main/xiaozhi-server/docker-compose_all.yml | 61 ++++++++++++++++------ 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/main/xiaozhi-server/docker-compose_all.yml b/main/xiaozhi-server/docker-compose_all.yml index 5b104470..e2f23bf1 100644 --- a/main/xiaozhi-server/docker-compose_all.yml +++ b/main/xiaozhi-server/docker-compose_all.yml @@ -1,25 +1,17 @@ # Docker安装全模块 -# 1、安装mysql -# |- 如果本机已经安装了MySQL,可以直接在数据库中创建名为`xiaozhi_esp32_server`的数据库。 -# |- 如果还没有MySQL,你可以通过docker安装mysql,执行以下一句话 -# |- docker run --name xiaozhi-esp32-server-db -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -e MYSQL_DATABASE=xiaozhi_esp32_server -e MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci" -d mysql:latest -# |- 记得修改下方SPRING_DATASOURCE_DRUID_URL的IP,ip不能写127.0.0.1或localhost,否则容器无法访问,要写你电脑局域网ip - -# 2、安装redis -# |- 如果本机已经安装了Redis,看一下你安装的redis端口、密码,然后修改下方redis的地址和端口 -# |- 如果还没有Redis,你可以通过docker安装redis,执行以下一句话 -# |- docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis -# |- 记得修改SPRING_DATA_REDIS_HOST的IP,ip不能写127.0.0.1或localhost,否则容器无法访问,要写你电脑局域网ip - - version: '3' services: # Server模块 xiaozhi-esp32-server: image: ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest container_name: xiaozhi-esp32-server + depends_on: + - xiaozhi-esp32-server-db + - xiaozhi-esp32-server-redis restart: always + networks: + - default ports: # ws服务端 - "8000:8000" @@ -38,14 +30,53 @@ services: image: ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest container_name: xiaozhi-esp32-server-web restart: always + networks: + - default + depends_on: + - xiaozhi-esp32-server-db + - xiaozhi-esp32-server-redis ports: # 智控台 - "8002:8002" environment: - TZ=Asia/Shanghai ##记得改mysql和redis IP 密码 - - SPRING_DATASOURCE_DRUID_URL=jdbc:mysql://192.168.1.25:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai + - SPRING_DATASOURCE_DRUID_URL=jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai - SPRING_DATASOURCE_DRUID_USERNAME=root - SPRING_DATASOURCE_DRUID_PASSWORD=123456 - - SPRING_DATA_REDIS_HOST=192.168.1.25 + - SPRING_DATA_REDIS_HOST=xiaozhi-esp32-server-redis - SPRING_DATA_REDIS_PORT=6379 + xiaozhi-esp32-server-db: + image: mysql:latest + container_name: xiaozhi-esp32-server-db + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + timeout: 45s + interval: 10s + retries: 10 + restart: always + networks: + - default + expose: + - 3306 + volumes: + - ./mysql/data:/var/lib/mysql + environment: + - TZ=Asia/Shanghai + - MYSQL_ROOT_PASSWORD=123456 + - MYSQL_DATABASE=xiaozhi_esp32_server + - MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci" + xiaozhi-esp32-server-redis: + image: redis + expose: + - 6379 + container_name: xiaozhi-esp32-server-redis + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - default +networks: + default: From ff9b3ed1a2162266b4e40c9cafc67215263c6dcf Mon Sep 17 00:00:00 2001 From: expoli <31023767+expoli@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:15:37 +0800 Subject: [PATCH 07/17] Update Deployment_all.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 docker compose 部署的时候,不应该每次更新的时候都把原来的 docker-compose 文件删除,再重新配置。 - 如果没有自己部署数据库需求的话,目前可以直接开箱即用,同步更新了 server 中的 Redis 与 MySQL 的镜像服务配置。 --- docs/Deployment_all.md | 44 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index 49804a57..0550a71d 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -71,22 +71,16 @@ xiaozhi-server 如果你的文件目录结构也是上面的,就继续往下。如果不是,你就再仔细看看是不是漏操作了什么。 -## 2. 安装Mysql和Redis +## 2. 安装Mysql和Redis(可选) ### 2.1 安装Mysql数据库 -如果本机已经安装了MySQL,可以直接在数据库中创建名为`xiaozhi_esp32_server`的数据库。 +如果本机已经安装了MySQL,可以直接在数据库中创建名为`xiaozhi_esp32_server`的数据库。然后把 docker-compose.yml 文件中的 xiaozhi-esp32-server-db 部分注释或者删除掉,同时修改 `jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai` xiaozhi-esp32-server-db 为自己的局域网 IP 地址。 ```sql CREATE DATABASE xiaozhi_esp32_server CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` -如果还没有MySQL,你可以通过docker安装mysql - -``` -docker run --name xiaozhi-esp32-server-db -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -e MYSQL_DATABASE=xiaozhi_esp32_server -e MYSQL_INITDB_ARGS="--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci" -d mysql:latest -``` - -### 2.2 安装Redis +### 2.2 安装Redis(可选) 如果还没有Redis,你可以通过docker安装redis @@ -94,7 +88,7 @@ docker run --name xiaozhi-esp32-server-db -e MYSQL_ROOT_PASSWORD=123456 -p 3306: docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis ``` -## 3. 配置docker-compose_all.yml文件文件 +### 2.3 配置docker-compose_all.yml文件使用本地的 MySQL 与 Redis 在`xiaozhi-server`目录,打开docker-compose_all.yml, @@ -104,20 +98,12 @@ docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis 3、确认一下mysql的用户名`SPRING_DATASOURCE_DRUID_USERNAME`和密码`SPRING_DATASOURCE_DRUID_PASSWORD` +## 3 配置docker-compose_all.yml(可选) + +你可以修改配置文件中的数据库密码环境变量 `SPRING_DATASOURCE_DRUID_PASSWORD` 与 `MYSQL_ROOT_PASSWORD` 为强密码。 + ## 4. 运行程序 -确认mysql和redis是否运行正常,输入 -``` -docker ps -``` -如果你能看到`xiaozhi-esp32-server-redis`和`xiaozhi-esp32-server-db`信息,就可以继续往下,如果看不到,说明你的`mysql`和`redis`没有安装或没有启动。需要继续回到上面的教程,看看哪一步漏了。 - -``` -CONTAINER ID IMAGE COMMAND CREATED TATUS PORTS NAMES -xxx redis "xx" xxx xxx 0.0.0.0:6379->6379/tcp xiaozhi-esp32-server-redis -xxx mysql:latest "xx" xxx xxx 0.0.0.0:3306->3306/tcp, 33060/tcp xiaozhi-esp32-server-db -``` - 接下来打开命令行工具,使用`终端`或`命令行`工具 进入到你的`xiaozhi-server`,执行以下命令 ``` @@ -154,17 +140,15 @@ http://localhost:8002/xiaozhi/doc.html ``` manager-api: - url: http://127.0.0.1:8002/xiaozhi + url: http://xiaozhi-esp32-server-web:8002/xiaozhi secret: 你的server.secret值 ``` 1、把你刚才从`智控台`复制过来的`server.secret`的`参数值`复制到`.config.yaml`文件里的`secret`里。 -2、把`url`里的`127.0.0.1`改成你电脑局域网内的ip - 类似这样的效果 ``` manager-api: - url: http://192.168.1.25:8002/xiaozhi + url: http://xiaozhi-esp32-server-web/xiaozhi secret: 12345678-xxxx-xxxx-xxxx-123456789000 ``` @@ -215,15 +199,13 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/ 5.1、执行以下命令 ``` -docker-compose down +docker compose down docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest +docker compose pull +docker compose up -d ``` -5.2、删掉`docker-compose_all.yaml`文件 - -5.3、重新按1.1开始部署 - # 方式二:本地源码运行全模块 ## 1.安装MySQL数据库 From 91278b0b3dd732dd26d8b274a73ad8dd96a3d1e7 Mon Sep 17 00:00:00 2001 From: expoli <31023767+expoli@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:16:25 +0800 Subject: [PATCH 08/17] Update Deployment_all.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MySQL 与 Redis 的安装是可选的 --- docs/Deployment_all.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index 0550a71d..b4e59912 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -71,7 +71,7 @@ xiaozhi-server 如果你的文件目录结构也是上面的,就继续往下。如果不是,你就再仔细看看是不是漏操作了什么。 -## 2. 安装Mysql和Redis(可选) +## 2. 手动安装Mysql和Redis(可选) ### 2.1 安装Mysql数据库 如果本机已经安装了MySQL,可以直接在数据库中创建名为`xiaozhi_esp32_server`的数据库。然后把 docker-compose.yml 文件中的 xiaozhi-esp32-server-db 部分注释或者删除掉,同时修改 `jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai` xiaozhi-esp32-server-db 为自己的局域网 IP 地址。 @@ -80,8 +80,7 @@ xiaozhi-server CREATE DATABASE xiaozhi_esp32_server CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` -### 2.2 安装Redis(可选) - +### 2.2 安装Redis 如果还没有Redis,你可以通过docker安装redis ``` From 6e4e9a002bacd0d3e2a10e68a44890b8725c5afb Mon Sep 17 00:00:00 2001 From: expoli <31023767+expoli@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:18:07 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=20docker=20=E7=9B=B4=E6=8E=A5=E9=9B=86?= =?UTF-8?q?=E6=88=90=E4=BA=86=20compose=20=E5=91=BD=E4=BB=A4=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E9=9C=80=E8=A6=81=E6=89=8B=E5=8A=A8=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E4=B8=8E=E6=89=A7=E8=A1=8C=20docker-compose=20=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E4=BA=86=EF=BC=8C=E6=8F=90=E9=AB=98=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Deployment_all.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index b4e59912..24a2eb8b 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -106,7 +106,7 @@ docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis 接下来打开命令行工具,使用`终端`或`命令行`工具 进入到你的`xiaozhi-server`,执行以下命令 ``` -docker-compose -f docker-compose_all.yml up -d +docker compose -f docker-compose_all.yml up -d ``` 执行完后,再执行以下命令,查看日志信息。 From 703ecfae4007c645646cd8bbe113c370970d0a11 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 14 Apr 2025 18:30:58 +0800 Subject: [PATCH 10/17] =?UTF-8?q?fix:manager=E4=B8=8B=E5=8F=91=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=AD=97=E7=AC=A6=E8=BD=AC=E6=95=B0=E5=AD=97bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Deployment.md | 5 ++- docs/Deployment_all.md | 7 ++++ main/xiaozhi-server/core/connection.py | 4 +-- .../core/handle/receiveAudioHandle.py | 4 +-- .../core/providers/tts/fishspeech.py | 16 ++++----- .../core/providers/tts/gpt_sovits_v2.py | 36 +++++++++++-------- .../core/providers/tts/gpt_sovits_v3.py | 27 ++++++++------ .../core/providers/tts/openai.py | 2 +- .../core/providers/tts/siliconflow.py | 2 +- .../core/providers/tts/tencent.py | 2 +- .../core/providers/tts/ttson.py | 10 +++--- .../core/providers/vad/silero.py | 4 +-- main/xiaozhi-server/core/utils/util.py | 4 +-- 13 files changed, 72 insertions(+), 51 deletions(-) diff --git a/docs/Deployment.md b/docs/Deployment.md index 0bbe629d..097aea59 100644 --- a/docs/Deployment.md +++ b/docs/Deployment.md @@ -137,7 +137,10 @@ docker logs -f xiaozhi-esp32-server ``` docker stop xiaozhi-esp32-server docker rm xiaozhi-esp32-server +docker stop xiaozhi-esp32-server-web +docker rm xiaozhi-esp32-server-web docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest +docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest ``` 5.3、重新按docker方式部署 @@ -221,7 +224,7 @@ python app.py ## 配置项目 如果你的`xiaozhi-server`目录没有`data`,你需要创建`data`目录。 -如果你的`data`下面没有`.config.yaml`文件,你可以把源码目录下的`config.yaml`文件复制一份,重命名为`.config.yaml` +如果你的`data`下面没有`.config.yaml`文件,你可以把`xiaozhi-server`目录下的`config.yaml`文件复制到`data`,并重命名为`.config.yaml` 修改`xiaozhi-server`下`data`目录下的`.config.yaml`文件,配置本项目必须的一个配置。 diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index e4ac4ff9..e18d66e1 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -212,6 +212,10 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/ ``` docker-compose down +docker stop xiaozhi-esp32-server +docker rm xiaozhi-esp32-server +docker stop xiaozhi-esp32-server-web +docker rm xiaozhi-esp32-server-web docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest ``` @@ -388,6 +392,9 @@ pip install -r requirements.txt `server.secret`需要说明一下,这个`参数值`很重要,作用是让我们的`Server`端连接`manager-api`。`server.secret`是每次从零部署manager模块时,会自动随机生成的密钥。 +如果你的`xiaozhi-server`目录没有`data`,你需要创建`data`目录。 +如果你的`data`下面没有`.config.yaml`文件,你可以把`xiaozhi-server`目录下的`config_from_api.yaml`文件复制到`data`,并重命名为`.config.yaml` + 复制`参数值`后,打开`xiaozhi-server`下的`data`目录的`.config.yaml`文件。此刻你的配置文件内容应该是这样的: ``` diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index fc191b6f..3a69d096 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -204,7 +204,7 @@ class ConnectionHandler: self.headers.get("device-id", None), self.headers.get("client-id", None), ) - private_config["delete_audio"] = self.config["delete_audio"] + private_config["delete_audio"] = bool(self.config["delete_audio"], True) self.logger.bind(tag=TAG).info(f"获取差异化配置成功: {private_config}") except Exception as e: self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}") @@ -702,7 +702,7 @@ class ConnectionHandler: opus_datas, text_index, tts_file = [], 0, None try: self.logger.bind(tag=TAG).debug("正在处理TTS任务...") - tts_timeout = self.config.get("tts_timeout", 10) + tts_timeout = int(self.config.get("tts_timeout", 10)) tts_file, text, text_index = future.result(timeout=tts_timeout) if text is None or len(text) <= 0: self.logger.bind(tag=TAG).error( diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index f798739a..c0ffa429 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -71,8 +71,8 @@ async def no_voice_close_connect(conn): conn.client_no_voice_last_time = time.time() * 1000 else: no_voice_time = time.time() * 1000 - conn.client_no_voice_last_time - close_connection_no_voice_time = conn.config.get( - "close_connection_no_voice_time", 120 + close_connection_no_voice_time = int( + conn.config.get("close_connection_no_voice_time", 120) ) if ( not conn.close_after_chat diff --git a/main/xiaozhi-server/core/providers/tts/fishspeech.py b/main/xiaozhi-server/core/providers/tts/fishspeech.py index 6d59243b..6456961d 100644 --- a/main/xiaozhi-server/core/providers/tts/fishspeech.py +++ b/main/xiaozhi-server/core/providers/tts/fishspeech.py @@ -89,19 +89,19 @@ class TTSProvider(TTSProviderBase): self.reference_audio = config.get("reference_audio", []) self.reference_text = config.get("reference_text", []) self.format = config.get("format", "wav") - self.channels = config.get("channels", 1) - self.rate = config.get("rate", 44100) + self.channels = int(config.get("channels", 1)) + self.rate = int(config.get("rate", 44100)) self.api_key = config.get("api_key", "YOUR_API_KEY") have_key = check_model_key("FishSpeech TTS", self.api_key) if not have_key: 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) - self.top_p = config.get("top_p", 0.7) - self.repetition_penalty = config.get("repetition_penalty", 1.2) - self.temperature = config.get("temperature", 0.7) - self.streaming = config.get("streaming", False) + self.max_new_tokens = int(config.get("max_new_tokens", 1024)) + self.chunk_length = int(config.get("chunk_length", 200)) + self.top_p = float(config.get("top_p", 0.7)) + self.repetition_penalty = float(config.get("repetition_penalty", 1.2)) + self.temperature = float(config.get("temperature", 0.7)) + self.streaming = bool(config.get("streaming", False)) self.use_memory_cache = config.get("use_memory_cache", "on") self.seed = config.get("seed") self.api_url = config.get("api_url", "http://127.0.0.1:8080/v1/tts") diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py index ae20beb6..311ef5b0 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py @@ -10,6 +10,7 @@ from core.providers.tts.base import TTSProviderBase TAG = __name__ logger = setup_logging() + class TTSProvider(TTSProviderBase): def __init__(self, config, delete_audio_file): super().__init__(config, delete_audio_file) @@ -18,23 +19,26 @@ class TTSProvider(TTSProviderBase): self.ref_audio_path = config.get("ref_audio_path") self.prompt_text = config.get("prompt_text") self.prompt_lang = config.get("prompt_lang", "zh") - self.top_k = config.get("top_k", 5) - self.top_p = config.get("top_p", 1) - self.temperature = config.get("temperature", 1) + self.top_k = int(config.get("top_k", 5)) + self.top_p = float(config.get("top_p", 1)) + self.temperature = float(config.get("temperature", 1)) self.text_split_method = config.get("text_split_method", "cut0") - self.batch_size = config.get("batch_size", 1) - self.batch_threshold = config.get("batch_threshold", 0.75) - self.split_bucket = config.get("split_bucket", True) - self.return_fragment = config.get("return_fragment", False) - self.speed_factor = config.get("speed_factor", 1.0) - self.streaming_mode = config.get("streaming_mode", False) - self.seed = config.get("seed", -1) - self.parallel_infer = config.get("parallel_infer", True) - self.repetition_penalty = config.get("repetition_penalty", 1.35) + self.batch_size = int(config.get("batch_size", 1)) + self.batch_threshold = float(config.get("batch_threshold", 0.75)) + self.split_bucket = bool(config.get("split_bucket", True)) + self.return_fragment = bool(config.get("return_fragment", False)) + self.speed_factor = float(config.get("speed_factor", 1.0)) + self.streaming_mode = bool(config.get("streaming_mode", False)) + self.seed = int(config.get("seed", -1)) + self.parallel_infer = bool(config.get("parallel_infer", True)) + self.repetition_penalty = float(config.get("repetition_penalty", 1.35)) self.aux_ref_audio_paths = config.get("aux_ref_audio_paths", []) def generate_filename(self, extension=".wav"): - return os.path.join(self.output_file, f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + return os.path.join( + self.output_file, + f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}", + ) async def text_to_speak(self, text, output_file): request_json = { @@ -56,7 +60,7 @@ class TTSProvider(TTSProviderBase): "streaming_mode": self.streaming_mode, "seed": self.seed, "parallel_infer": self.parallel_infer, - "repetition_penalty": self.repetition_penalty + "repetition_penalty": self.repetition_penalty, } resp = requests.post(self.url, json=request_json) @@ -64,4 +68,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error(f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}") + logger.bind(tag=TAG).error( + f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}" + ) diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py index ebfb12f1..25c52b84 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py @@ -8,6 +8,7 @@ from core.providers.tts.base import TTSProviderBase TAG = __name__ logger = setup_logging() + class TTSProvider(TTSProviderBase): def __init__(self, config, delete_audio_file): super().__init__(config, delete_audio_file) @@ -16,18 +17,20 @@ class TTSProvider(TTSProviderBase): self.prompt_text = config.get("prompt_text") self.prompt_language = config.get("prompt_language") self.text_language = config.get("text_language", "audo") - self.top_k = config.get("top_k", 15) - self.top_p = config.get("top_p", 1.0) - self.temperature = config.get("temperature", 1.0) - self.cut_punc = config.get("cut_punc","") - self.speed = config.get("speed", 1.0) - self.inp_refs = config.get("inp_refs",[]) - self.sample_steps = config.get("sample_steps",32) - self.if_sr = config.get("if_sr",False) - + self.top_k = int(config.get("top_k", 15)) + self.top_p = float(config.get("top_p", 1.0)) + self.temperature = float(config.get("temperature", 1.0)) + self.cut_punc = config.get("cut_punc", "") + self.speed = float(config.get("speed", 1.0)) + self.inp_refs = config.get("inp_refs", []) + self.sample_steps = int(config.get("sample_steps", 32)) + self.if_sr = bool(config.get("if_sr", False)) def generate_filename(self, extension=".wav"): - return os.path.join(self.output_file, f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}") + return os.path.join( + self.output_file, + f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}", + ) async def text_to_speak(self, text, output_file): request_params = { @@ -51,4 +54,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error(f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}") + logger.bind(tag=TAG).error( + f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}" + ) diff --git a/main/xiaozhi-server/core/providers/tts/openai.py b/main/xiaozhi-server/core/providers/tts/openai.py index 18daf29b..a5d48057 100644 --- a/main/xiaozhi-server/core/providers/tts/openai.py +++ b/main/xiaozhi-server/core/providers/tts/openai.py @@ -21,7 +21,7 @@ class TTSProvider(TTSProviderBase): else: self.voice = config.get("voice", "alloy") self.response_format = "wav" - self.speed = config.get("speed", 1.0) + self.speed = float(config.get("speed", 1.0)) self.output_file = config.get("output_dir", "tmp/") check_model_key("TTS", self.api_key) diff --git a/main/xiaozhi-server/core/providers/tts/siliconflow.py b/main/xiaozhi-server/core/providers/tts/siliconflow.py index 76d131bb..be6b9e2a 100644 --- a/main/xiaozhi-server/core/providers/tts/siliconflow.py +++ b/main/xiaozhi-server/core/providers/tts/siliconflow.py @@ -16,7 +16,7 @@ class TTSProvider(TTSProviderBase): self.voice = config.get("voice") self.response_format = config.get("response_format") self.sample_rate = config.get("sample_rate") - self.speed = config.get("speed") + self.speed = float(config.get("speed")) self.gain = config.get("gain") self.host = "api.siliconflow.cn" diff --git a/main/xiaozhi-server/core/providers/tts/tencent.py b/main/xiaozhi-server/core/providers/tts/tencent.py index c05261fd..f2501e2f 100644 --- a/main/xiaozhi-server/core/providers/tts/tencent.py +++ b/main/xiaozhi-server/core/providers/tts/tencent.py @@ -19,7 +19,7 @@ class TTSProvider(TTSProviderBase): if config.get("private_voice"): self.voice = config.get("private_voice") else: - self.voice = config.get("voice") + self.voice = int(config.get("voice")) self.api_url = "https://tts.tencentcloudapi.com" # 正确的API端点 self.region = config.get("region") self.output_file = config.get("output_dir") diff --git a/main/xiaozhi-server/core/providers/tts/ttson.py b/main/xiaozhi-server/core/providers/tts/ttson.py index ba0a1222..a6401141 100644 --- a/main/xiaozhi-server/core/providers/tts/ttson.py +++ b/main/xiaozhi-server/core/providers/tts/ttson.py @@ -20,13 +20,13 @@ class TTSProvider(TTSProviderBase): self.voice_id = int(config.get("voice_id", 1695)) self.token = config.get("token") self.to_lang = config.get("to_lang") - self.volume_change_dB = config.get("volume_change_dB", 0) - self.speed_factor = config.get("speed_factor", 1) - self.stream = config.get("stream", False) + self.volume_change_dB = int(config.get("volume_change_dB", 0)) + self.speed_factor = int(config.get("speed_factor", 1)) + self.stream = bool(config.get("stream", False)) self.output_file = config.get("output_dir") - self.pitch_factor = config.get("pitch_factor", 0) + self.pitch_factor = int(config.get("pitch_factor", 0)) self.format = config.get("format", "mp3") - self.emotion = config.get("emotion", 1) + self.emotion = int(config.get("emotion", 1)) self.header = {"Content-Type": "application/json"} def generate_filename(self, extension=".mp3"): diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index df05cd6d..3332d9ac 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -21,8 +21,8 @@ class VADProvider(VADProviderBase): (get_speech_timestamps, _, _, _, _) = self.utils self.decoder = opuslib_next.Decoder(16000, 1) - self.vad_threshold = config.get("threshold") - self.silence_threshold_ms = config.get("min_silence_duration_ms") + self.vad_threshold = float(config.get("threshold", 0.5)) + self.silence_threshold_ms = int(config.get("min_silence_duration_ms", 1000)) def is_vad(self, conn, opus_packet): try: diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 07a2c985..dd56f5ae 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -230,7 +230,7 @@ def initialize_modules( modules["tts"] = tts.create_instance( tts_type, config["TTS"][config["selected_module"]["TTS"]], - config["delete_audio"], + bool(config.get("delete_audio", True)), ) logger.bind(tag=TAG).info(f"初始化组件: tts成功") @@ -294,7 +294,7 @@ def initialize_modules( modules["asr"] = asr.create_instance( asr_type, config["ASR"][config["selected_module"]["ASR"]], - config["delete_audio"], + bool(config.get("delete_audio", True)), ) logger.bind(tag=TAG).info(f"初始化组件: asr成功") From c76f28824c4fbbe29b200435e1ab11f38955d6f7 Mon Sep 17 00:00:00 2001 From: haotian Date: Mon, 14 Apr 2025 18:49:24 +0800 Subject: [PATCH 11/17] =?UTF-8?q?update:=20=E4=BC=98=E5=8C=96=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E6=A8=A1=E5=9D=97=E6=89=93=E5=8D=B0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config/logger.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/config/logger.py b/main/xiaozhi-server/config/logger.py index 5981822c..cc87807b 100644 --- a/main/xiaozhi-server/config/logger.py +++ b/main/xiaozhi-server/config/logger.py @@ -25,6 +25,12 @@ def build_module_string(selected_module): ) +def formatter(record): + """为没有 tag 的日志添加默认值""" + record["extra"].setdefault("tag", record["name"]) + return record["message"] + + def setup_logging(): """从配置文件中读取日志配置,并设置日志输出格式和级别""" config = load_config() @@ -56,9 +62,9 @@ def setup_logging(): logger.remove() # 输出到控制台 - logger.add(sys.stdout, format=log_format, level=log_level) + logger.add(sys.stdout, format=log_format, level=log_level, filter=formatter) # 输出到文件 - logger.add(os.path.join(log_dir, log_file), format=log_format_file, level=log_level) + logger.add(os.path.join(log_dir, log_file), format=log_format_file, level=log_level, filter=formatter) return logger From 6284a74a6ec0b17bb39f70378b969c8f6af891be Mon Sep 17 00:00:00 2001 From: haotian Date: Mon, 14 Apr 2025 21:43:55 +0800 Subject: [PATCH 12/17] =?UTF-8?q?update:=20=E4=BF=AE=E5=A4=8DDify=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E6=9C=AA=E8=BF=87=E6=BB=A4=E5=85=A8=E9=87=8F=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/llm/dify/dify.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/dify/dify.py b/main/xiaozhi-server/core/providers/llm/dify/dify.py index 26bb48b7..030aba05 100644 --- a/main/xiaozhi-server/core/providers/llm/dify/dify.py +++ b/main/xiaozhi-server/core/providers/llm/dify/dify.py @@ -58,7 +58,8 @@ class LLMProvider(LLMProviderBase): self.session_conversation_map[session_id] = ( conversation_id # 更新映射 ) - if event.get("answer"): + # 过滤 message_replace 事件,此事件会全量推一次 + if event.get("event") != "message_replace" and event.get("answer"): yield event["answer"] elif self.mode == "workflows/run": for line in r.iter_lines(): @@ -73,7 +74,8 @@ class LLMProvider(LLMProviderBase): for line in r.iter_lines(): if line.startswith(b"data: "): event = json.loads(line[6:]) - if event.get("answer"): + # 过滤 message_replace 事件,此事件会全量推一次 + if event.get("event") != "message_replace" and event.get("answer"): yield event["answer"] except Exception as e: From 03d89d22ec1ac07824b5481094cc578bc3e6be63 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 14 Apr 2025 21:53:52 +0800 Subject: [PATCH 13/17] =?UTF-8?q?update:=E9=80=9A=E8=BF=87=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E8=BD=AC=E5=8F=91=E6=8E=A5=E5=8F=A3api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-web/.env.development | 5 +---- main/manager-web/vue.config.js | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/main/manager-web/.env.development b/main/manager-web/.env.development index ced1a9e7..0a1de306 100644 --- a/main/manager-web/.env.development +++ b/main/manager-web/.env.development @@ -1,4 +1 @@ -# 暂时使用群主的接口 -# VUE_APP_API_BASE_URL=https://2662r3426b.vicp.fun/xiaozhi -# 如果本地开发,请使用以下接口 -VUE_APP_API_BASE_URL=http://localhost:8002/xiaozhi \ No newline at end of file +VUE_APP_API_BASE_URL=/xiaozhi \ No newline at end of file diff --git a/main/manager-web/vue.config.js b/main/manager-web/vue.config.js index e29c0de7..5df17711 100644 --- a/main/manager-web/vue.config.js +++ b/main/manager-web/vue.config.js @@ -15,6 +15,10 @@ module.exports = defineConfig({ devServer: { port: 8001, // 指定端口为 8001 proxy: { + '/xiaozhi': { + target: 'http://127.0.0.1:8002', + changeOrigin: true + } }, client: { overlay: false, // 不显示 webpack 错误覆盖层 From 25da9fe119998f7db260dc78ecec3e2eb569ada7 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Mon, 14 Apr 2025 22:09:18 +0800 Subject: [PATCH 14/17] =?UTF-8?q?fix=EF=BC=9Anumber=E7=86=9F=E7=9F=A5?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=B0=8F=E6=95=B0=E7=82=B9=E5=90=8E=E7=9A=84?= =?UTF-8?q?00?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/config/service/impl/ConfigServiceImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java index 78a7b511..7be38a89 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java @@ -171,7 +171,13 @@ public class ConfigServiceImpl implements ConfigService { switch (param.getValueType().toLowerCase()) { case "number": try { - current.put(lastKey, Double.parseDouble(value)); + double doubleValue = Double.parseDouble(value); + // 如果数值是整数形式,则转换为Integer + if (doubleValue == (int) doubleValue) { + current.put(lastKey, (int) doubleValue); + } else { + current.put(lastKey, doubleValue); + } } catch (NumberFormatException e) { current.put(lastKey, value); } From 6cf6b0d6457f7998228c5a5fd98200f6aa4d9f75 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Tue, 15 Apr 2025 00:22:53 +0800 Subject: [PATCH 15/17] =?UTF-8?q?update:=E4=BC=98=E5=8C=96docker=E5=90=AF?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- docs/Deployment_all.md | 70 +++++++--------------- main/xiaozhi-server/docker-compose_all.yml | 10 ++-- 3 files changed, 31 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 0a684e6a..57f377d3 100644 --- a/.gitignore +++ b/.gitignore @@ -158,4 +158,5 @@ my_wakeup_words.mp3 main/manager-api/.vscode # Ignore webpack cache directory -main/manager-web/.webpack_cache/ \ No newline at end of file +main/manager-web/.webpack_cache/ +main/xiaozhi-server/mysql diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index d48a1796..9223c7fc 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -71,39 +71,34 @@ xiaozhi-server 如果你的文件目录结构也是上面的,就继续往下。如果不是,你就再仔细看看是不是漏操作了什么。 -## 2. 手动安装Mysql和Redis(可选) +## 2. 备份数据 -### 2.1 安装Mysql数据库 -如果本机已经安装了MySQL,可以直接在数据库中创建名为`xiaozhi_esp32_server`的数据库。然后把 docker-compose.yml 文件中的 xiaozhi-esp32-server-db 部分注释或者删除掉,同时修改 `jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai` xiaozhi-esp32-server-db 为自己的局域网 IP 地址。 +如果你之前已经成功运行智控台,如果上面保存有你的密钥信息,请先从智控台上拷贝重要数据下来。因为升级过程中,有可能会覆盖原来的数据。 -```sql -CREATE DATABASE xiaozhi_esp32_server CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -``` - -### 2.2 安装Redis -如果还没有Redis,你可以通过docker安装redis +## 3. 清除历史版本镜像和容器 +接下来打开命令行工具,使用`终端`或`命令行`工具 进入到你的`xiaozhi-server`,执行以下命令 ``` -docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis +docker compose -f docker-compose_all.yml down + +docker stop xiaozhi-esp32-server +docker rm xiaozhi-esp32-server + +docker stop xiaozhi-esp32-server-web +docker rm xiaozhi-esp32-server-web + +docker stop xiaozhi-esp32-server-db +docker rm xiaozhi-esp32-server-db + +docker stop xiaozhi-esp32-server-redis +docker rm xiaozhi-esp32-server-redis + +docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest +docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest ``` -### 2.3 配置docker-compose_all.yml文件使用本地的 MySQL 与 Redis - -在`xiaozhi-server`目录,打开docker-compose_all.yml, - -1、修改`SPRING_DATASOURCE_DRUID_URL`里,把`192.168.1.25`修改成部署mysql的电脑局域网ip,如果是你本地,你要查看一下你本机的局域网ip是什么 - -2、修改`SPRING_DATA_REDIS_HOST`里,把`192.168.1.25`修改成部署redis的电脑局域网ip,如果是你本地,你要查看一下你本机的局域网ip是什么 - -3、确认一下mysql的用户名`SPRING_DATASOURCE_DRUID_USERNAME`和密码`SPRING_DATASOURCE_DRUID_PASSWORD` - -## 3 配置docker-compose_all.yml(可选) - -你可以修改配置文件中的数据库密码环境变量 `SPRING_DATASOURCE_DRUID_PASSWORD` 与 `MYSQL_ROOT_PASSWORD` 为强密码。 - ## 4. 运行程序 - -接下来打开命令行工具,使用`终端`或`命令行`工具 进入到你的`xiaozhi-server`,执行以下命令 +执行以下命令启动新版本容器 ``` docker compose -f docker-compose_all.yml up -d @@ -139,10 +134,11 @@ http://localhost:8002/xiaozhi/doc.html ``` manager-api: - url: http://xiaozhi-esp32-server-web:8002/xiaozhi + url: http://127.0.0.1:8002/xiaozhi secret: 你的server.secret值 ``` 1、把你刚才从`智控台`复制过来的`server.secret`的`参数值`复制到`.config.yaml`文件里的`secret`里。 +2、注意,把`url`改成下面的`http://xiaozhi-esp32-server-web/xiaozhi` 类似这样的效果 ``` @@ -189,26 +185,6 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/ 接下来,你就可以开始 [编译esp32固件](firmware-build.md)了。 -## 6. 版本升级操作 - -如果后期想升级版本,先备份你的模型密钥。 - -进入`xiaozhi-server`目录 - -5.1、执行以下命令 - -``` -docker-compose down -docker stop xiaozhi-esp32-server -docker rm xiaozhi-esp32-server -docker stop xiaozhi-esp32-server-web -docker rm xiaozhi-esp32-server-web -docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest -docker rmi ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest -docker compose pull -docker compose up -d -``` - # 方式二:本地源码运行全模块 ## 1.安装MySQL数据库 diff --git a/main/xiaozhi-server/docker-compose_all.yml b/main/xiaozhi-server/docker-compose_all.yml index e2f23bf1..81b227b1 100644 --- a/main/xiaozhi-server/docker-compose_all.yml +++ b/main/xiaozhi-server/docker-compose_all.yml @@ -33,15 +33,17 @@ services: networks: - default depends_on: - - xiaozhi-esp32-server-db - - xiaozhi-esp32-server-redis + xiaozhi-esp32-server-db: + condition: service_healthy + xiaozhi-esp32-server-redis: + condition: service_healthy ports: # 智控台 - "8002:8002" environment: - TZ=Asia/Shanghai ##记得改mysql和redis IP 密码 - - SPRING_DATASOURCE_DRUID_URL=jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai + - SPRING_DATASOURCE_DRUID_URL=jdbc:mysql://xiaozhi-esp32-server-db:3306/xiaozhi_esp32_server?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&connectTimeout=30000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false&maxReconnects=10 - SPRING_DATASOURCE_DRUID_USERNAME=root - SPRING_DATASOURCE_DRUID_PASSWORD=123456 - SPRING_DATA_REDIS_HOST=xiaozhi-esp32-server-redis @@ -58,7 +60,7 @@ services: networks: - default expose: - - 3306 + - "3306:3306" volumes: - ./mysql/data:/var/lib/mysql environment: From 11c9915a703ab3e4e9ad707720bd13281f877fa2 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Tue, 15 Apr 2025 00:44:34 +0800 Subject: [PATCH 16/17] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/connection.py | 2 +- main/xiaozhi-server/core/websocket_server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index b86cc24f..146241ff 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -204,7 +204,7 @@ class ConnectionHandler: self.headers.get("device-id", None), self.headers.get("client-id", None), ) - private_config["delete_audio"] = bool(self.config["delete_audio"], True) + private_config["delete_audio"] = bool(self.config.get("delete_audio", True)) self.logger.bind(tag=TAG).info(f"获取差异化配置成功: {private_config}") except Exception as e: self.logger.bind(tag=TAG).error(f"获取差异化配置失败: {e}") diff --git a/main/xiaozhi-server/core/websocket_server.py b/main/xiaozhi-server/core/websocket_server.py index ed0b92ee..cb4dee08 100644 --- a/main/xiaozhi-server/core/websocket_server.py +++ b/main/xiaozhi-server/core/websocket_server.py @@ -25,7 +25,7 @@ class WebSocketServer: async def start(self): server_config = self.config["server"] host = server_config["ip"] - port = server_config["port"] + port = int(server_config.get("port", 8000)) self.logger.bind(tag=TAG).info( "Server is running at ws://{}:{}/xiaozhi/v1/", get_local_ip(), port From eaaea2b24c2847af0b290575b85389de719626e2 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Tue, 15 Apr 2025 01:46:36 +0800 Subject: [PATCH 17/17] =?UTF-8?q?update:=E6=99=BA=E6=8E=A7=E5=8F=B0?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=89=88=E6=9C=AC=E5=8F=B7=20(#793)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/security/config/ShiroConfig.java | 1 + .../security/controller/LoginController.java | 10 +++++ main/manager-web/src/apis/module/user.js | 18 +++++++- .../src/components/VersionFooter.vue | 39 ++++++++++++++++ .../src/views/DeviceManagement.vue | 25 ++++------- main/manager-web/src/views/ModelConfig.vue | 44 +++++-------------- .../src/views/ParamsManagement.vue | 16 ------- main/manager-web/src/views/UserManagement.vue | 18 +------- main/manager-web/src/views/home.vue | 9 ++-- main/manager-web/src/views/login.vue | 28 +++++++++--- main/manager-web/src/views/register.vue | 8 ++-- main/manager-web/src/views/roleConfig.vue | 3 -- main/xiaozhi-server/config/logger.py | 9 +++- 13 files changed, 126 insertions(+), 102 deletions(-) create mode 100644 main/manager-web/src/components/VersionFooter.vue diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java index 7bd356fc..fdcb62b2 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java @@ -76,6 +76,7 @@ public class ShiroConfig { filterMap.put("/favicon.ico", "anon"); filterMap.put("/user/captcha", "anon"); filterMap.put("/user/login", "anon"); + filterMap.put("/user/pub-config", "anon"); filterMap.put("/user/register", "anon"); filterMap.put("/config/server-base", "anon"); filterMap.put("/config/agent-models", "anon"); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java index 7db612b5..0ee6a1ce 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/controller/LoginController.java @@ -1,6 +1,8 @@ package xiaozhi.modules.security.controller; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -111,4 +113,12 @@ public class LoginController { sysUserTokenService.changePassword(userId, passwordDTO); return new Result<>(); } + + @GetMapping("/pub-config") + @Operation(summary = "公共配置") + public Result> pubConfig() { + Map config = new HashMap<>(); + config.put("version", "0.3.3"); + return new Result>().ok(config); + } } \ No newline at end of file diff --git a/main/manager-web/src/apis/module/user.js b/main/manager-web/src/apis/module/user.js index 748867b2..84d5b7f9 100755 --- a/main/manager-web/src/apis/module/user.js +++ b/main/manager-web/src/apis/module/user.js @@ -106,7 +106,7 @@ export default { }, // 修改用户状态 changeUserStatus(status, userIds, successCallback) { - console.log(555,userIds) + console.log(555, userIds) RequestService.sendRequest() .url(`${getServiceUrl()}/admin/users/changeStatus/${status}`) .method('put') @@ -122,4 +122,20 @@ export default { }) }).send() }, + // 获取公共配置 + getPubConfig(callback) { + RequestService.sendRequest() + .url(`${getServiceUrl()}/user/pub-config`) + .method('GET') + .success((res) => { + RequestService.clearRequestTime(); + callback(res); + }) + .fail((err) => { + console.error('获取公共配置失败:', err); + RequestService.reAjaxFun(() => { + this.getPubConfig(callback); + }); + }).send(); + }, } diff --git a/main/manager-web/src/components/VersionFooter.vue b/main/manager-web/src/components/VersionFooter.vue new file mode 100644 index 00000000..8a8b76a0 --- /dev/null +++ b/main/manager-web/src/components/VersionFooter.vue @@ -0,0 +1,39 @@ + + + + + \ No newline at end of file diff --git a/main/manager-web/src/views/DeviceManagement.vue b/main/manager-web/src/views/DeviceManagement.vue index bffefb07..823bedc6 100644 --- a/main/manager-web/src/views/DeviceManagement.vue +++ b/main/manager-web/src/views/DeviceManagement.vue @@ -4,7 +4,8 @@

设备列表

- + @@ -40,22 +41,17 @@
- {{ isAllSelected ? '取消全选' : '全选' }} + {{ isAllSelected ? '取消全选' : '全选' }} - 新增 + 新增
- @@ -63,9 +59,6 @@
-
@@ -82,7 +75,7 @@ export default { data() { return { addDeviceDialogVisible: false, - selectedDevices: [], + selectedDevices: [], isAllSelected: false, currentAgentId: this.$route.query.agentId || '', currentPage: 1, @@ -99,7 +92,7 @@ export default { return this.deviceList.slice(start, end); }, pageCount() { - return Math.ceil(this.deviceList.length / this.pageSize); + return Math.ceil(this.deviceList.length / this.pageSize); }, visiblePages() { const pages = []; @@ -346,6 +339,4 @@ export default { color: white; } } - - diff --git a/main/manager-web/src/views/ModelConfig.vue b/main/manager-web/src/views/ModelConfig.vue index 343cb06f..2f5a5dae 100644 --- a/main/manager-web/src/views/ModelConfig.vue +++ b/main/manager-web/src/views/ModelConfig.vue @@ -3,21 +3,15 @@
-

{{ modelTypeText }}

+

{{ modelTypeText }}

-
- - - 搜索 - -
+
+ + + 搜索 + +
@@ -93,9 +87,9 @@ @@ -639,20 +629,6 @@ export default { gap: 8px; } -.copyright { - text-align: center; - color: #979db1; - font-size: 12px; - font-weight: 400; - margin-top: auto; - padding: 30px 0 20px; - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - width: 100%; -} - .title-wrapper { display: flex; align-items: center; diff --git a/main/manager-web/src/views/ParamsManagement.vue b/main/manager-web/src/views/ParamsManagement.vue index e9baa1e8..02f912a1 100644 --- a/main/manager-web/src/views/ParamsManagement.vue +++ b/main/manager-web/src/views/ParamsManagement.vue @@ -64,8 +64,6 @@ - - @@ -382,20 +380,6 @@ export default { } } -.copyright { - text-align: center; - color: #979db1; - font-size: 12px; - font-weight: 400; - margin-top: auto; - padding: 30px 0 20px; - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - width: 100%; -} - .custom-pagination { display: flex; align-items: center; diff --git a/main/manager-web/src/views/UserManagement.vue b/main/manager-web/src/views/UserManagement.vue index 45015d97..617ad04c 100644 --- a/main/manager-web/src/views/UserManagement.vue +++ b/main/manager-web/src/views/UserManagement.vue @@ -42,7 +42,7 @@
- {{ isAllSelected ? '取消全选' : '全选' }} + {{ isAllSelected ? '取消全选' : '全选' }} 启用
- - @@ -465,20 +463,6 @@ export default { color: black; } -.copyright { - text-align: center; - color: #979db1; - font-size: 12px; - font-weight: 400; - margin-top: auto; - padding: 30px 0 20px; - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - width: 100%; -} - .custom-pagination { display: flex; align-items: center; diff --git a/main/manager-web/src/views/home.vue b/main/manager-web/src/views/home.vue index 0e9fd32a..5f8115f3 100644 --- a/main/manager-web/src/views/home.vue +++ b/main/manager-web/src/views/home.vue @@ -35,11 +35,11 @@ @deviceManage="handleDeviceManage" @delete="handleDeleteAgent" /> - + + + @@ -49,10 +49,11 @@ import Api from '@/apis/api'; import AddWisdomBodyDialog from '@/components/AddWisdomBodyDialog.vue'; import DeviceItem from '@/components/DeviceItem.vue'; import HeaderBar from '@/components/HeaderBar.vue'; +import VersionFooter from '@/components/VersionFooter.vue'; export default { name: 'HomePage', - components: { DeviceItem, AddWisdomBodyDialog, HeaderBar }, + components: { DeviceItem, AddWisdomBodyDialog, HeaderBar, VersionFooter }, data() { return { addDeviceDialogVisible: false, diff --git a/main/manager-web/src/views/login.vue b/main/manager-web/src/views/login.vue index 23c8479e..af50bc1c 100644 --- a/main/manager-web/src/views/login.vue +++ b/main/manager-web/src/views/login.vue @@ -51,9 +51,7 @@ - + @@ -61,11 +59,14 @@