From b05a03e1989c8a4eb7d23cc526100359437565ff Mon Sep 17 00:00:00 2001
From: hubian <908234780@qq.com>
Date: Sat, 11 Apr 2026 12:22:03 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20Matrix=E8=BF=9E=E6=8E=A5=E6=94=B9?=
=?UTF-8?q?=E4=B8=BA=E9=9D=9E=E9=98=BB=E5=A1=9E=E6=A8=A1=E5=BC=8F=EF=BC=8C?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8=E9=98=BB?=
=?UTF-8?q?=E5=A1=9E=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ai_chat.db | Bin 61440 -> 61440 bytes
logs/ai-chat.log | 81 +++---------------
reset_config.py | 25 ++++++
.../__pycache__/ai_service.cpython-310.pyc | Bin 2956 -> 3308 bytes
.../matrix_service.cpython-310.pyc | Bin 5038 -> 5803 bytes
services/ai_service.py | 67 ++++++++-------
services/matrix_service.py | 36 ++++++--
7 files changed, 104 insertions(+), 105 deletions(-)
create mode 100644 reset_config.py
diff --git a/ai_chat.db b/ai_chat.db
index be46afd048ccc1f3c0cfa8ed1a2890b1b2bb2ec2..4aa5ce0578900f5ffbbff6f0fe2fe6c0bd8a4e15 100644
GIT binary patch
delta 815
zcmZ{izi-n(6vxlC8`Y8QXVWrNAd#U`hN|wJ@6Nv#C@d@p78Wyv(j=;=M5<6x(k0
psEokb
z8h2l=HaDs%JTES>aey7mcd*C&)M@ALL;_4K>Qd@>p*#Zj9nPP-t$Hhk=S3b)1MUZ$
zTg1n<!POCgDVm9Y4O+OO!_whQ5Q~6CA-loWc&gPt4LAh>vbh-5E}#V9I4F_jFY<
zd6603SGmXx4=D@C+%(K1SsNgIUvFrGEX4SKYRTXMmK220K@@$PDZ(ElLm$B}_yPSa
z#6iA7+^<}l6IF)T!&QU>Q5_?Q;WK>5LR7aQjvIGV=6n>D-|i{}33>>6!g>c!9!e@&
zl+xdZRvIr}RcPI1HD0YRFH=X!znS=B**rM2{qs?cO^P#$c~LZy;0(c8Vy>TpDWoi`
cYL-4ha3cDChwC%)qB_>FyI$(f;6xc`M!}5oKb_F~eEEjS8~r5J
zOl_Ii7!*adLFy6Xw|8fZF$Sk;d!%+8aXLKUV`6|%X2oZA(aVo#C87GPFnojX6mhtGLj?{nN;`B5k6^WzEo&|t5Ycb6xH
zY1?ke4QxA&lQzNV68W(G#uy02IP#^&zfU3Kd*^
zD0!(+NfE>#Q0FzkM7l-*69p_pjKTk$UYFo-seq7H!PPde-D>{)=E|Eh%ZtCSAgcWX
zaiwwlkFjrGrVZi#2j0QdGC}ACZ)AjVp}M7EFRSt-G(H)K
zbBJK)2$4uv%G3#%o%c$^?#LEs^06yjM$}UB4yn5BF>V`Tg;`|2?~E{C=$r)=UdrQX
zX(#Lj^WPx*KZLXN2H-bFcyA!h!%;?5LWyq<;ZVb&ReDKDqH0a1S||g0`kXQZxFUko
zn3HP?HKC>go-$2PYgVqQP}kt=Bu&xutXk8eemf%UH=z$=RUD<-ALwHS?WCJ$q`hQ-
z(5|E8YPP0_`j`p(sD>KdGNZ)pH6t|U6LkV}Gv=*2shOckyJ3D?A@{`dJO!knT`Ez@
z^y*gzztUdk%`Ndsmvj
zSXjC%2b=pM>gT;;xxfO~
zEBOzMmzl#E&phaK%H@Kacd)r{%=bz+WRdQ#1fw2zt5It;bueDku9K5xwmclA=J!_q
z0U`igK>^@+{zWoi#WD(Wg)?ClowIh}jkA(p=|BV`h3_B8fVS_;aJY8JGGs`D2X08h
z2;3s`D#0KOZ;Ubrgv%Ey&Id3Z^*~~Mj=Cz0Rq|B2p%!=7zC8ZWy7lqCOy#SQBfeUi
zSI;%y|I^Z)nZ`Q{uxa=2zP0k+mDWN8^j3(wfA_-Yw;&RIQO_v{D5bzSZE^f
z%!1y*0XjATy9533!IwF^5O
z+@Mgnov4rpLALyNQK3NFL7~0~x|9MwO2Bu}CeS4XbV-d^2uc(xhsf(1=#TmX;P>gR
zREv}fnW}?QY1DvTkg+l~X#$i=hq?m&!Id0ol~ywXbxfljbknLLX`o0o)S5A!c7|H0
zhX$yX8mSffUaKX-#Jo`_FOcfdJ@61Y3c6CWLTlcnU3H0H4K2EbYHz}9YsoN4yT@hj
zN~E->`fQMxx58vys-@`GFm;CTOF=S(axa;dXwSIHFU2)fH&i8;)a67o9HotKL$e*sAn(QEQ)s?`2G7(<{v|Bp<1MSLBfbuY
znK?zCLGM@4>_CG=6zZ$ThfeZmpjg%M%vUz@Kn~(U@);lExQGft7?Z!UD1Bm7(K=JDsTLyfa?VNxJ{IDY|~5kI~M&0aL$0wYu$P?)$5;5X+lqUR+vPrMG2
z$Qld*So*>59|HqyRJ-9tg^W|-jj)lCz{&sgm`?*+E#NCg$WYo2brb&8sSa#;B>!7|
z^xUJ}kE&ZYGIVvUY!Y8`C8x-2TUa){8fm3~<&=!crZ8Ls2iBtJj!8_ZNAF4OcqVGkriEeK)XUp;v|~aKCd+PVWZei+=aDEH
z_~-(sNWc~yiymP7D2^KGE)FBC$B)$iHy2^P6ff|*cz}HTP9>&-hyiADhho|P11jEz
AEC2ui
literal 2956
zcmZuz-ESMm5#PJp``}TOWI0x%)<{{kNW<0=Ejx7*p-oI%a;(;&lFDsh2I=9#T}daA
zJbLeFS?sZh;W!1_stJ&wC?Y!$fgp`vVk2l#r0B;(fj;-O`&v+z|A9WZMctV@l9FXF
zvBTM!*{|98&77af*aX^3fB&cV;Q%53!ol9A!QdqH;Kx81DG{bHwW5?1_}40GRV!
zTB23Gq$`QeC>gPhSu(k~W${#7DcOvc(o8Q6Fr$=ti7=B{Zxd!+RZD|>@G2<{F?E6D
zQjM>`CU#+N`}#)b=k4r7cIWohJ0E=7`S`cnpSC+6{i5*7oP8HeNHM2Ldd>?%NoTxr
zD6RQ===n9b(mcEzhV|)ue(L#SlT#<2n*{po^wjf5k3O5fG$rX}Xp*{(&@baUdz%S^
zlhA`@Ae_JrNl9e~n94P#L8n|>G?>nexAhX;C&l~FlFmk$%?)mj5til_8;ECQ*dWYE
z^)!nr8)Cy?Z8HV#d{Cwea|>Kt^2)p@Ew}DDZ@K}OCUUEMUFubz@k*J(p?VE@75ZuD
z!8njescRvLh@(Vm-3nFUv`DLGLJFMD2<)R2bA}wAnLShZ;kok;IMO*)SeO+S%$7rY
z#owR3AW|sJ^rSD(&&-|^7$!HVqyeui#?xY}L`=jmXz$JCm;EZwU-sNue$ub=TDjt`
z<=1?%8r0n~pD4pkL^961-b8uX4f8>A^#BA;m!GV!OUrS*nio3G2rL{JKy1ZU29@SW
z-&>PClZSkR=CN;H047qv2P8tDD3JzK4Ye)Glt|emOpUZna)Y)NvF1HaJ*pn_~~
z{%-r%x9?utxbx@F8V5m8m`etoKYX6(rn61!NH%_}?-q0VJ`YKw;x8?6A*oke^u-{o
z5W6fP2Vf{OjXHBf?v(x7qPO%ohypY-R6{Y8QRoIZp!xXj^&hZ5BdsbA0(XfA(g;Gq
z-KsPM4;!LZMhN#BzAW~?K@V;NxdQH4A>az-hT77=A!G#{b3tn_
znR#7r>7lklnZ;5Y$}?n+F#9a|@adKj87n%tmqt2EZ)l0UWk%+f(I((rbIWX#mK6ag
z02~?UgV2Y@$ZHVE%%+5rtKC@&gzHpy<>1vdcpCg&fX-}qkm1H1IJTYbwj@J9%7_!h
z&R;io+Q01F`eNtSjlM~rDTt+DxZj()rW7JAZk<^Rqwp&HBs_;#b*s
zkiuMN^P|qk8=ZHqbw2x|Z+q`RK1NB|uhuIZQ(SP*JjGqXMfTpXTd!BVvWw#UO5oRS
zsxnh(gv-A0nz6Jwa5_2m#QF6)zq1&(?HBL<19II>J;tsW1#b!rH<=E3wa$eLLBpk8
zbuT-izshR?`WssD-H#gS)`Psb$Q+y_NO-J{EQFv+AjK>I6^%g!HKGfuG=Qqr
z(j$FEX9JMC09Y)uL1Vz7?g9c1p>>Qj&mM1R=!RES
z@v=f{$D2=u)>bOA+Da?M4n(O-M2OIifFC1Q6*jU;g-AwhWQ=U3*+GxA)t1d3i)^%w
zfEyz}0x-TtE}ozyv{nYzTRn(gJh2AS6QE=1KBVDnuyZB};eVkG7xe
zwM9+tP~$1^bRQsHsKuWNy(G|AiZL)1UqbR_B!_{V7f&LK9~vTyGD19qn&*&UW!_U}(ipvH0u|y#Pz5J}K>X83#s6XDF(^6%fB_1qu$3oZ
zY{5T$YY-*()qm6b-@HG0zj^3EbadCxX~kkOr<}>9v7Xo5Dt8=dI}TLIMg{o+$GO;W
zD_xC-2giCZ4k1B=39Ma`zUliF@ogLzknCP5=CFJU$Y=za0KZc#@`N&~AGSxIjODpu
zX*v$`%Z?L2jx@scx>sA;-9q3eRq`MvR%%en6uic{xN3`2sKv4`5at5E2t)=6?kerw
jPnSm>O0jK9$
diff --git a/services/__pycache__/matrix_service.cpython-310.pyc b/services/__pycache__/matrix_service.cpython-310.pyc
index e6d6fb2b62f3de3748cfa3ee65aa973552e68c03..c2447825c4c24657b6457d34772da365f697c28a 100644
GIT binary patch
delta 1876
zcmZ8hU2GIp6ux(6c4u~X_NUuYC@oDPB+aG>h(V-A!D=Fq&>}TKK$$Fir*%uayWE)t
z+HAWeHl{(qCKpT$HrWyph0q0RB&JAAeBr^wN5ej-4{lo?)x;O0c+MSQtFxIiXU^ZA
z^PTU`Z2R?RBH3^_NZ@My{z1C9=2qkfO2@=cbiLKg4BOwQumB5A_zvkT#LNkD$Y5bR
zz#?|APh(LQo6ru0Se<=69MZ=whF9K*2Gprg&5O&Nup)$
zpt4${zYEvjR_t-L0vRB1Q>HTQoLV5358nKID@wS17D_5L-Bn5kLqEMlrj;2=2?MVc
zH~`OHtl}#LL561NZo>3kWR$vQfx6-M6y7UyjsNDpRZ9kUgaryu3qwhzmuSR|4xgfA
zRH0-fDr15L<;kBPAP3=ZMwKxIV?FI8JxLNF@s9rbDxr!2?KayvmQ
zL8(}|r2B4u4Zy!wx~q3?FU{Ut{AMr;(U@p5It)GU*xX7p@v70^
z@&cqO($fRqq@
zpX;B&@#@erpvyfVehT2HKqerLnFmOw8Q=&EcL5x#gafPk3+g2peOmQm01#zV9aA$!
zpQj)mZm<+8kdiq=Fi4g(o-~sPIc=SvkNz_EPOS&aJrmtu7NXqd13O-
z;`Q>vC)cWz(-?U6qwaL9oschRMR(Ifb~f+kSJe_Gr;AU^RcFsGOitxjFZb4*pWa&f
z@=9l0z7E_Q+Nw9-uil(qDqmcjzq&YoV|fp^~~pqFvsTkW)#n&
zz#b~`BxB-2prc!JhBGPt96Gk4Lr6f&k_&z%rYdR!jli#!8so~Nh!&wE4Iah1
zvhK!Oa*9IYlS=^wzXjjH?M#61h{Jb|Gyo~+0FZ|%=a3Xdbr*pZswItS@|4MU;Kn-O
zhHtnNxB)93X>|Q19Zu@g<0Lcu%QJu}>L?wf8K3t=q96kujj3>$;7;fsHKxv2KfLKt
zqguRoZSj0LJy{PRy$FqVKg2om4ImyLB)O-D7d~Dpmpj`Y`oNJe_)2(qtJ;CuP7oEJ
zGicj6xtQ7l6NmN;
z(QGfuNbP8r4fFk2Qj*S0Wiuyi?j+rGHsfV>VEKSI&@K$zjba0cij1~^RVUIZn
z8)(;ulDS+u)6W^2{s+LzV9O99M#HC}pGV;b&4@-qABPX~Y1rAhE9hdWC_=?AX!+?b6L@vHI
nj;u0>L5+YBjjcBt;~_B2v?sZ*XL~M_7ST<030^8~ThX@_{AnAY0RfBMKYZ
zOis8#UKE2oG6zHn8C!H9(-}BHzK0=O7G1~|#8aYsgUz_YoMK+@c7^w9@JKa{!>bdC
z)?{-G7hK~_=Ia<4a+na8?i&f;G)0E;H`qIWf%p&Ht;0zP`1Y3b5
z--N`z#^w36miqi}EoPmw!ffs*u_d2k8cPbx9{^k7z^=L^5>}J{xA`=C8@E~EA92#Q
zqa|2kURK5CIB)bTu*%NhqX$3y{>3**^nOtH)ZgYC;HvZ1kXo~9rC~g2zGm|rXux?9
zjEv&h$yJWDhU-UM@rwxSfW}#D2*5Eh_S5f8JfyiyqHUo&%FpS7i_B*vNl-*1{%sty>$IJDq
zUyEe07)X!HUdo+N)A>U~;~2&^0$($@1_m!?x-(ZhFH{m4emEK>!*NoPiBAo-4?3Sf
zzGXJzK>DGOr`4UpNf=d63Io;{cJOpLu6kXshkCZ`R5l;Pab-4;qe#gy^^W^GjH%o1
z5jd@Wbq7{crFJC}%km5!@eO6u~s&5|-QuGqSn~bTY;yU{ItGLrQgN&DvG)K$+*R`K4F9h1jAMNla>GiXw*~J%P6sN6q7_ir%^
wtgPi050#wKiLMUYl|`hzzLx3xBC7g+%dF7VhLL(u{?d4l%16}2u1OgD2asI^?EnA(
diff --git a/services/ai_service.py b/services/ai_service.py
index eb6357f..494c70e 100644
--- a/services/ai_service.py
+++ b/services/ai_service.py
@@ -2,45 +2,40 @@
AI服务 - 调用大模型API
"""
import httpx
-from typing import List, Dict, Optional
+from typing import List, Dict, AsyncGenerator
import json
import logging
logger = logging.getLogger(__name__)
-# 默认配置
-DEFAULT_API_BASE = "http://192.168.2.17:19007/v1"
-DEFAULT_API_KEY = "xxxx"
-DEFAULT_MODEL = "auto"
-
class AIService:
- def __init__(self, api_base: str = None, api_key: str = None, model: str = None):
- self.api_base = api_base or DEFAULT_API_BASE
- self.api_key = api_key or DEFAULT_API_KEY
- self.model = model or DEFAULT_MODEL
+ def __init__(self):
+ self.api_base = ""
+ self.api_key = ""
+ self.model = ""
+ self.use_mock = True
def update_config(self, api_base: str, api_key: str, model: str):
"""更新配置"""
self.api_base = api_base
self.api_key = api_key
self.model = model
- logger.info(f"AI配置已更新: {api_base}, model={model}")
- self.api_base = api_base
- self.api_key = api_key
- self.model = model
+ # 如果配置完整则使用真实API,否则使用mock
+ self.use_mock = not (api_base and model)
+ logger.info(f"AI配置已更新: api_base={api_base}, model={model}, use_mock={self.use_mock}")
- async def chat(self, messages: List[Dict], stream: bool = False) -> str:
+ async def chat(self, messages: List[Dict]) -> str:
"""
调用AI模型进行对话
-
- Args:
- messages: 对话历史 [{"role": "user", "content": "..."}]
- stream: 是否流式输出
-
- Returns:
- AI回复内容
"""
+ # 如果使用mock模式,返回模拟回复
+ if self.use_mock:
+ logger.info("使用Mock模式回复")
+ last_msg = messages[-1]['content'] if messages else "你好"
+ return f"这是一个测试回复。您说的是:{last_msg}\n\n请配置有效的AI服务地址和模型,才能获得真正的AI回复。"
+
+ # 调用真实API
url = f"{self.api_base}/chat/completions"
headers = {
"Authorization": f"Bearer {self.api_key}",
@@ -49,21 +44,35 @@ class AIService:
payload = {
"model": self.model,
"messages": messages,
- "stream": stream,
"temperature": 0.7,
"max_tokens": 2000
}
- async with httpx.AsyncClient(timeout=60.0) as client:
- response = await client.post(url, headers=headers, json=payload)
- response.raise_for_status()
- data = response.json()
- return data['choices'][0]['message']['content']
+ logger.info(f"调用AI API: {url}, model={self.model}")
+
+ try:
+ async with httpx.AsyncClient(timeout=60.0) as client:
+ response = await client.post(url, headers=headers, json=payload)
+ response.raise_for_status()
+ data = response.json()
+ return data['choices'][0]['message']['content']
+ except Exception as e:
+ logger.error(f"AI API调用失败: {e}")
+ # API失败时返回模拟回复
+ last_msg = messages[-1]['content'] if messages else "你好"
+ return f"AI服务暂时不可用(错误:{str(e)})。您说的是:{last_msg}"
- async def chat_stream(self, messages: List[Dict]):
+ async def chat_stream(self, messages: List[Dict]) -> AsyncGenerator[str, None]:
"""
流式调用AI模型
"""
+ if self.use_mock:
+ last_msg = messages[-1]['content'] if messages else "你好"
+ reply = f"这是一个测试回复。您说的是:{last_msg}"
+ for char in reply:
+ yield char
+ return
+
url = f"{self.api_base}/chat/completions"
headers = {
"Authorization": f"Bearer {self.api_key}",
diff --git a/services/matrix_service.py b/services/matrix_service.py
index 63f8af9..567474f 100644
--- a/services/matrix_service.py
+++ b/services/matrix_service.py
@@ -45,12 +45,18 @@ class MatrixBot:
return False
try:
- self.client = AsyncClient(self.homeserver, self.username)
+ # 创建客户端
+ self.client = AsyncClient(
+ self.homeserver,
+ self.username,
+ store_path="/tmp/matrix_store"
+ )
- # 如果有access_token,直接使用
+ # 如果有access_token,直接设置
if self.access_token:
self.client.access_token = self.access_token
- logger.info(f"Matrix连接成功(使用token): {self.username}")
+ self.client.user_id = self.username
+ logger.info(f"Matrix已设置access_token: {self.username}")
self.is_running = True
return True
@@ -71,6 +77,7 @@ class MatrixBot:
async def start_sync(self, message_handler: Callable = None):
"""开始同步消息"""
if not self.client:
+ logger.warning("Matrix客户端未初始化")
return
self.on_message_callback = message_handler
@@ -78,8 +85,27 @@ class MatrixBot:
# 注册消息处理器
self.client.add_event_callback(self._handle_room_message, RoomMessageText)
- # 开始同步循环
- await self.client.sync_forever(timeout=30000)
+ # 首先执行一次同步获取房间信息
+ try:
+ sync_response = await self.client.sync(timeout=10000)
+ logger.info(f"Matrix初始同步完成")
+ except Exception as e:
+ logger.warning(f"Matrix初始同步失败: {e}, 将尝试继续")
+
+ # 启动后台同步任务(不阻塞)
+ asyncio.create_task(self._sync_loop())
+ logger.info("Matrix同步任务已启动")
+
+ async def _sync_loop(self):
+ """后台同步循环"""
+ while self.is_running:
+ try:
+ # 使用较短的超时,避免长时间阻塞
+ await self.client.sync(timeout=5000)
+ await asyncio.sleep(1) # 每秒同步一次
+ except Exception as e:
+ logger.warning(f"Matrix同步错误: {e}")
+ await asyncio.sleep(5) # 出错后等待5秒再重试
async def _handle_room_message(self, room: MatrixRoom, event: RoomMessageText):
"""处理收到的房间消息"""