myagent-ai 1.15.84 → 1.15.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/memory/manager.py CHANGED
@@ -231,7 +231,15 @@ class MemoryManager:
231
231
  params.append(timestamp())
232
232
 
233
233
  where = " AND ".join(conditions) if conditions else "1=1"
234
- sql = f"SELECT * FROM memories WHERE {where} ORDER BY {order_by} LIMIT ?"
234
+ # 安全白名单:仅允许预定义的排序字段
235
+ _allowed_orders = {
236
+ "created_at ASC", "created_at DESC",
237
+ "importance ASC", "importance DESC",
238
+ "access_count ASC", "access_count DESC",
239
+ "updated_at ASC", "updated_at DESC",
240
+ }
241
+ _safe_order = order_by if order_by in _allowed_orders else "created_at ASC"
242
+ sql = f"SELECT * FROM memories WHERE {where} ORDER BY {_safe_order} LIMIT ?"
235
243
  params.append(limit)
236
244
 
237
245
  rows = conn.execute(sql, params).fetchall()
@@ -512,8 +520,8 @@ class MemoryManager:
512
520
  return None # 空内容不查重
513
521
 
514
522
  conn = self._get_conn()
515
- conditions = [f"category = '{category}'"]
516
- params: list = []
523
+ conditions = ["category = ?"]
524
+ params: list = [category]
517
525
 
518
526
  if session_id:
519
527
  conditions.append("session_id = ?")
@@ -868,10 +876,13 @@ class MemoryManager:
868
876
  time_decay=time_decay, half_life_days=half_life_days)
869
877
  else:
870
878
  # 混合模式:取两种搜索结果的加权和 + 时间衰减
879
+ # 注意:传入 update_access=False 避免子搜索重复更新 access_count
871
880
  keyword_results = self._search_keyword(conn, query, where, params, limit * 2,
872
- time_decay=False, half_life_days=half_life_days)
881
+ time_decay=False, half_life_days=half_life_days,
882
+ update_access=False)
873
883
  semantic_results = self._search_semantic(conn, query, where, params, limit * 2,
874
- time_decay=False, half_life_days=half_life_days)
884
+ time_decay=False, half_life_days=half_life_days,
885
+ update_access=False)
875
886
 
876
887
  # 合并评分(相关性 + 时间衰减)
877
888
  combined: Dict[str, Tuple[MemoryEntry, float]] = {}
@@ -920,6 +931,7 @@ class MemoryManager:
920
931
  limit: int,
921
932
  time_decay: bool = False,
922
933
  half_life_days: float = 30.0,
934
+ update_access: bool = True,
923
935
  ) -> List[MemoryEntry]:
924
936
  """关键词 LIKE 搜索(支持时间衰减重排序)"""
925
937
  like_pattern = f"%{query}%"
@@ -949,12 +961,13 @@ class MemoryManager:
949
961
  entries = [e for e, _ in scored[:limit]]
950
962
 
951
963
  # 更新访问计数
952
- for entry in entries:
953
- conn.execute(
954
- "UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
955
- (entry.id,),
956
- )
957
- conn.commit()
964
+ if update_access:
965
+ for entry in entries:
966
+ conn.execute(
967
+ "UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
968
+ (entry.id,),
969
+ )
970
+ conn.commit()
958
971
 
959
972
  return entries
960
973
 
@@ -967,6 +980,7 @@ class MemoryManager:
967
980
  limit: int,
968
981
  time_decay: bool = False,
969
982
  half_life_days: float = 30.0,
983
+ update_access: bool = True,
970
984
  ) -> List[MemoryEntry]:
971
985
  """TF-IDF 语义搜索(支持时间衰减重排序)"""
972
986
  # 先取一批候选文档
@@ -1001,12 +1015,13 @@ class MemoryManager:
1001
1015
  result = [MemoryEntry.from_row(row_map[doc_id]) for doc_id in top_ids if doc_id in row_map]
1002
1016
 
1003
1017
  # 更新访问计数
1004
- for entry in result:
1005
- conn.execute(
1006
- "UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
1007
- (entry.id,),
1008
- )
1009
- conn.commit()
1018
+ if update_access:
1019
+ for entry in result:
1020
+ conn.execute(
1021
+ "UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
1022
+ (entry.id,),
1023
+ )
1024
+ conn.commit()
1010
1025
 
1011
1026
  return result
1012
1027
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.15.84",
3
+ "version": "1.15.85",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -127,10 +127,21 @@ class WebSearchSkill(Skill):
127
127
  r.raise_for_status()
128
128
  except requests.exceptions.SSLError:
129
129
  logger.warning("DuckDuckGo SSL 验证失败,跳过证书验证重试")
130
- r = requests.get("https://html.duckduckgo.com/html/", params={"q": query}, headers=_HEADERS, timeout=15, verify=False)
131
- r.raise_for_status()
130
+ try:
131
+ r = requests.get("https://html.duckduckgo.com/html/", params={"q": query}, headers=_HEADERS, timeout=15, verify=False)
132
+ r.raise_for_status()
133
+ except Exception as ssl_retry_err:
134
+ logger.error(f"DuckDuckGo SSL 重试也失败: {ssl_retry_err}")
135
+ return ""
136
+ except Exception as req_err:
137
+ logger.error(f"DuckDuckGo 请求失败: {req_err}")
138
+ return ""
132
139
  if r is not None:
133
- return r.text or ""
140
+ try:
141
+ return r.text or ""
142
+ except Exception as read_err:
143
+ logger.error(f"DuckDuckGo 读取响应失败: {read_err}")
144
+ return ""
134
145
  return ""
135
146
 
136
147
  html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
@@ -181,10 +192,21 @@ class WebSearchSkill(Skill):
181
192
  r.raise_for_status()
182
193
  except requests.exceptions.SSLError:
183
194
  logger.warning("Bing SSL 验证失败,跳过证书验证重试")
184
- r = requests.get("https://www.bing.com/search", params={"q": query, "count": num}, headers=_HEADERS, timeout=15, verify=False)
185
- r.raise_for_status()
195
+ try:
196
+ r = requests.get("https://www.bing.com/search", params={"q": query, "count": num}, headers=_HEADERS, timeout=15, verify=False)
197
+ r.raise_for_status()
198
+ except Exception as ssl_retry_err:
199
+ logger.error(f"Bing SSL 重试也失败: {ssl_retry_err}")
200
+ return ""
201
+ except Exception as req_err:
202
+ logger.error(f"Bing 请求失败: {req_err}")
203
+ return ""
186
204
  if r is not None:
187
- return r.text or ""
205
+ try:
206
+ return r.text or ""
207
+ except Exception as read_err:
208
+ logger.error(f"Bing 读取响应失败: {read_err}")
209
+ return ""
188
210
  return ""
189
211
 
190
212
  html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
@@ -266,12 +288,23 @@ class WebReadSkill(Skill):
266
288
  r.raise_for_status()
267
289
  except requests.exceptions.SSLError:
268
290
  logger.warning(f"web_read SSL 验证失败 ({url}),跳过证书验证重试")
269
- r = requests.get(url, headers=_HEADERS, timeout=30, verify=False)
270
- r.raise_for_status()
291
+ try:
292
+ r = requests.get(url, headers=_HEADERS, timeout=30, verify=False)
293
+ r.raise_for_status()
294
+ except Exception as ssl_retry_err:
295
+ logger.error(f"web_read SSL 重试也失败 ({url}): {ssl_retry_err}")
296
+ return None
297
+ except Exception as req_err:
298
+ logger.error(f"web_read 请求失败 ({url}): {req_err}")
299
+ return None
271
300
  if r is not None:
272
- r.encoding = r.apparent_encoding
273
- return r.text or ""
274
- return ""
301
+ try:
302
+ r.encoding = r.apparent_encoding
303
+ return r.text or ""
304
+ except Exception as read_err:
305
+ logger.error(f"web_read 读取响应内容失败 ({url}): {read_err}")
306
+ return None
307
+ return None
275
308
 
276
309
  html = await asyncio.wait_for(
277
310
  loop.run_in_executor(None, _fetch), timeout=60
@@ -323,6 +356,7 @@ class WebReadSkill(Skill):
323
356
  except asyncio.TimeoutError:
324
357
  return SkillResult(success=False, error=f"网页读取超时 (60s): {url}")
325
358
  except Exception as e:
359
+ logger.error(f"web_read 异常 ({url}): {e}", exc_info=True)
326
360
  return SkillResult(success=False, error=f"网页读取失败: {e}")
327
361
 
328
362
 
@@ -369,23 +403,33 @@ class URLReadSkill(Skill):
369
403
  )
370
404
  except requests.exceptions.SSLError:
371
405
  logger.warning(f"url_read SSL 验证失败 ({url}),跳过证书验证重试")
372
- r = requests.request(
373
- method=method,
374
- url=url,
375
- headers=headers,
376
- data=body,
377
- timeout=30,
378
- verify=False,
379
- )
406
+ try:
407
+ r = requests.request(
408
+ method=method,
409
+ url=url,
410
+ headers=headers,
411
+ data=body,
412
+ timeout=30,
413
+ verify=False,
414
+ )
415
+ except Exception as ssl_retry_err:
416
+ logger.error(f"url_read SSL 重试也失败 ({url}): {ssl_retry_err}")
417
+ return None
418
+ except Exception as req_err:
419
+ logger.error(f"url_read 请求失败 ({url}): {req_err}")
420
+ return None
380
421
  if r is not None:
381
422
  return r
382
- raise requests.exceptions.ConnectionError(f"请求失败: {url}")
423
+ return None
383
424
 
384
425
  response = await asyncio.wait_for(loop.run_in_executor(None, _request), timeout=60)
385
426
 
427
+ if response is None:
428
+ return SkillResult(success=False, error=f"请求失败: 无法连接到 {url}")
429
+
386
430
  try:
387
431
  content = response.json()
388
- except ValueError:
432
+ except (ValueError, AttributeError):
389
433
  content = response.text[:20000]
390
434
 
391
435
  return SkillResult(
@@ -398,4 +442,5 @@ class URLReadSkill(Skill):
398
442
  message=f"HTTP {response.status_code}",
399
443
  )
400
444
  except Exception as e:
445
+ logger.error(f"url_read 异常 ({url}): {e}", exc_info=True)
401
446
  return SkillResult(success=False, error=f"请求失败: {e}")