myagent-ai 1.19.8 → 1.20.0

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.
@@ -1500,6 +1500,32 @@ class MainAgent(BaseAgent):
1500
1500
 
1501
1501
  # 循环正常结束(max_iter 耗尽)时兜底保存
1502
1502
  else:
1503
+ logger.warning(f"[{task_id}] 达到最大迭代次数 ({max_iter}),任务未完成")
1504
+ # 通知前端:达到迭代上限
1505
+ await self._emit_v2_event("v2_iter_limit", {
1506
+ "message": f"任务已达到最大迭代次数 ({max_iter}),已自动保存为未完成任务",
1507
+ "iterations": self._iteration_count,
1508
+ "max_iterations": max_iter,
1509
+ "task_id": task_id,
1510
+ }, stream_callback)
1511
+ # 将未完成任务保存到任务持久化队列
1512
+ try:
1513
+ from core.task_persistence import TaskPersistence
1514
+ _tp = TaskPersistence()
1515
+ _tp.initialize()
1516
+ _tp.save_task(
1517
+ task_id=task_id,
1518
+ description=context.user_input or "未完成任务",
1519
+ session_id=context.session_id or "",
1520
+ agent_path=context.agent_path or "",
1521
+ status="pending",
1522
+ metadata={"iterations": self._iteration_count, "reason": "达到最大迭代次数"},
1523
+ last_message=f"达到最大迭代次数 {max_iter},共执行 {self._iteration_count} 次",
1524
+ )
1525
+ logger.info(f"[{task_id}] 已将未完成任务保存到任务队列")
1526
+ except Exception as _tp_err:
1527
+ logger.warning(f"[{task_id}] 保存未完成任务失败: {_tp_err}")
1528
+
1503
1529
  if self.memory and _v2_reasoning_collected:
1504
1530
  _fallback_text = "\n".join(_v2_reasoning_collected)
1505
1531
  if _fallback_text.strip():
package/config.py CHANGED
@@ -71,7 +71,7 @@ class ExecutorConfig:
71
71
  @dataclass
72
72
  class AgentConfig:
73
73
  """Agent 配置"""
74
- max_iterations: int = 30 # 单任务最大迭代次数
74
+ max_iterations: int = 10000 # 单任务最大迭代次数
75
75
  max_parallel: int = 3 # 最大并行任务数
76
76
  verbose: bool = True # 详细日志
77
77
 
@@ -107,8 +107,7 @@ DEPENDENCIES: List[DepInfo] = [
107
107
  note="PyTorch 音频处理库 (SenseVoice 必需)"),
108
108
  DepInfo("funasr", "funasr", "1.1.0", "stt", "all",
109
109
  note="[v1.18.8] SenseVoice 中文语音识别(首选,阿里达摩院)"),
110
- DepInfo("faster_whisper", "faster-whisper", "1.0.0", "stt", "all",
111
- note="Whisper 本地语音识别引擎 (备选,需 C++ 编译)"),
110
+
112
111
  DepInfo("speech_recognition", "SpeechRecognition", "3.10.0", "stt", "all",
113
112
  note="在线语音识别 (Google API,纯 Python 无需编译,Termux 兼容)"),
114
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.19.8",
3
+ "version": "1.20.0",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -6,5 +6,5 @@
6
6
  funasr>=1.1.0
7
7
  torch>=2.0.0
8
8
  torchaudio>=2.0.0
9
- faster-whisper>=1.0.0
9
+
10
10
  pydub>=0.25.1
package/setup.py CHANGED
@@ -41,7 +41,6 @@ setup(
41
41
  "funasr>=1.1.0",
42
42
  "torch>=2.0.0",
43
43
  "torchaudio>=2.0.0",
44
- "faster-whisper>=1.0.0",
45
44
  # 浏览器自动化 (ChromeDev MCP, 无需 Playwright)
46
45
  # 桌面 GUI 自动化 (内置技能)
47
46
  "pynput>=1.7.6",
@@ -53,7 +52,7 @@ setup(
53
52
  "discord": ["discord.py>=2.3.0"],
54
53
  "anthropic": ["anthropic>=0.18.0"],
55
54
  "communication": ["cryptography>=41.0.0", "websockets>=12.0"],
56
- "voice": ["funasr>=1.1.0", "torch>=2.0.0", "torchaudio>=2.0.0", "faster-whisper>=1.0.0"],
55
+ "voice": ["funasr>=1.1.0", "torch>=2.0.0", "torchaudio>=2.0.0"],
57
56
  "all": [
58
57
  "python-telegram-bot>=21.0",
59
58
  "discord.py>=2.3.0",
@@ -63,7 +62,6 @@ setup(
63
62
  "funasr>=1.1.0",
64
63
  "torch>=2.0.0",
65
64
  "torchaudio>=2.0.0",
66
- "faster-whisper>=1.0.0",
67
65
  ],
68
66
  },
69
67
  entry_points={
package/web/api_server.py CHANGED
@@ -1549,11 +1549,10 @@ window.toggleFullscreen = function() {{
1549
1549
 
1550
1550
  接受音频文件(WAV/WEBM/OGG),使用本地 STT 引擎转录。
1551
1551
  支持的引擎(按优先级):
1552
- 1. [v1.18.7] SenseVoice(推荐,中文识别最佳,需:pip install funasr torch torchaudio)
1553
- 2. faster-whisper(备选,需安装:pip install faster-whisper
1554
- 3. vosk(备选,需安装:pip install vosk)
1555
- 4. LLM API Whisper 兼容端点
1556
- 5. SpeechRecognition(Google,需外网)
1552
+ 1. SenseVoice(推荐,中文识别最佳,需:pip install funasr torch torchaudio)
1553
+ 2. vosk(备选,需安装:pip install vosk
1554
+ 3. LLM API Whisper 兼容端点
1555
+ 4. SpeechRecognition(Google,需外网)
1557
1556
  """
1558
1557
  try:
1559
1558
  reader = await request.multipart()
@@ -1639,117 +1638,6 @@ window.toggleFullscreen = function() {{
1639
1638
  except Exception as e:
1640
1639
  logger.warning(f"SenseVoice 转录失败: {e}")
1641
1640
 
1642
- # ── 尝试 faster-whisper ──
1643
- try:
1644
- whisper_model = self._whisper_model
1645
- if whisper_model is None:
1646
- # 预加载未完成或未安装,尝试懒加载
1647
- import warnings as _w
1648
- _w.filterwarnings("ignore", message=".*HF_TOKEN.*", category=UserWarning)
1649
- _w.filterwarnings("ignore", message=".*huggingface_hub.*token.*", category=UserWarning)
1650
- _w.filterwarnings("ignore", message=".*ffmpeg or avconv.*", category=RuntimeWarning)
1651
- os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1")
1652
- os.environ.setdefault("HF_HUB_DISABLE_PROGRESS_BARS", "1")
1653
- os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
1654
- from faster_whisper import WhisperModel
1655
- model_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'models', 'whisper')
1656
- self._whisper_model = WhisperModel("tiny", device="cpu", compute_type="int8",
1657
- download_root=model_dir)
1658
- whisper_model = self._whisper_model
1659
- logger.info("faster-whisper tiny 模型已加载 (CPU int8)")
1660
-
1661
- # faster-whisper 需要 16kHz WAV
1662
- # [v1.15.8] 使用 pydub+ffmpeg 正确转换 WebM/Opus/OGG 等格式
1663
- wav_buf = io.BytesIO()
1664
- try:
1665
- from pydub import AudioSegment
1666
- audio_buf = io.BytesIO(audio_data)
1667
- seg = AudioSegment.from_file(audio_buf, format=audio_format or "webm")
1668
- seg = seg.set_channels(1).set_frame_rate(16000).set_sample_width(2)
1669
- seg.export(wav_buf, format="wav")
1670
- wav_buf.seek(0)
1671
- except Exception as _pydub_err:
1672
- # pydub 不可用时 fallback:仅处理已是 WAV 的情况
1673
- import wave
1674
- audio_buf = io.BytesIO(audio_data)
1675
- try:
1676
- with wave.open(audio_buf, 'rb') as rf:
1677
- wav_buf = io.BytesIO()
1678
- with wave.open(wav_buf, 'wb') as wf:
1679
- wf.setnchannels(1)
1680
- wf.setsampwidth(2)
1681
- wf.setframerate(16000)
1682
- frames = rf.readframes(rf.getnframes())
1683
- wf.writeframes(frames)
1684
- wav_buf.seek(0)
1685
- except Exception:
1686
- logger.warning(f"音频格式转换失败(pydub: {_pydub_err})")
1687
- return web.json_response({"error": "音频格式不支持,需要 WAV 或安装 pydub+ffmpeg"}, status=400)
1688
-
1689
- wav_buf.seek(0)
1690
- segments, info = whisper_model.transcribe(wav_buf, beam_size=1,
1691
- language="zh",
1692
- initial_prompt="以下是普通话的句子",
1693
- vad_filter=True, vad_parameters=dict(
1694
- min_silence_duration_ms=300))
1695
- text = "".join(seg.text for seg in segments).strip()
1696
-
1697
- if text:
1698
- return web.json_response({"text": text, "engine": "faster-whisper"})
1699
- except ImportError:
1700
- logger.debug("faster-whisper 未安装,尝试自动安装...")
1701
- try:
1702
- from core.deps_checker import ensure_skill_deps
1703
- installed = ensure_skill_deps("stt")
1704
- if installed:
1705
- logger.info("faster-whisper 自动安装成功,重新尝试转录")
1706
- from faster_whisper import WhisperModel
1707
- import os
1708
- model_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'models', 'whisper')
1709
- self._whisper_model = WhisperModel("tiny", device="cpu", compute_type="int8",
1710
- download_root=model_dir)
1711
- whisper_model = self._whisper_model
1712
- # 重新执行转录(跳过上面的 try 已完成的逻辑,直接进入转录流程)
1713
- import io
1714
- # [v1.15.8] 使用 pydub+ffmpeg 正确转换音频格式
1715
- wav_buf = io.BytesIO()
1716
- try:
1717
- from pydub import AudioSegment
1718
- audio_buf = io.BytesIO(audio_data)
1719
- seg = AudioSegment.from_file(audio_buf, format=audio_format or "webm")
1720
- seg = seg.set_channels(1).set_frame_rate(16000).set_sample_width(2)
1721
- seg.export(wav_buf, format="wav")
1722
- wav_buf.seek(0)
1723
- except Exception as _pydub_err2:
1724
- import wave
1725
- audio_buf = io.BytesIO(audio_data)
1726
- try:
1727
- with wave.open(audio_buf, 'rb') as rf:
1728
- wav_buf = io.BytesIO()
1729
- with wave.open(wav_buf, 'wb') as wf:
1730
- wf.setnchannels(1)
1731
- wf.setsampwidth(2)
1732
- wf.setframerate(16000)
1733
- frames = rf.readframes(rf.getnframes())
1734
- wf.writeframes(frames)
1735
- wav_buf.seek(0)
1736
- except Exception:
1737
- logger.warning(f"音频格式转换失败(pydub: {_pydub_err2})")
1738
- return web.json_response({"error": "音频格式不支持"}, status=400)
1739
- wav_buf.seek(0)
1740
- segments, info = whisper_model.transcribe(wav_buf, beam_size=1,
1741
- language="zh",
1742
- initial_prompt="以下是普通话的句子",
1743
- vad_filter=True, vad_parameters=dict(
1744
- min_silence_duration_ms=300))
1745
- text = "".join(seg.text for seg in segments).strip()
1746
- if text:
1747
- return web.json_response({"text": text, "engine": "faster-whisper"})
1748
- except Exception as inst_err:
1749
- logger.warning(f"faster-whisper 自动安装/转录失败: {inst_err}")
1750
- except Exception as e:
1751
- logger.warning(f"faster-whisper 转录失败: {e}")
1752
-
1753
1641
  # ── 尝试 vosk ──
1754
1642
  try:
1755
1643
  import vosk
@@ -1872,9 +1760,8 @@ window.toggleFullscreen = function() {{
1872
1760
  "error": "未检测到可用的 STT 引擎。请尝试以下方案:\n"
1873
1761
  " 1. pip install funasr torch torchaudio (SenseVoice,中文最佳,推荐)\n"
1874
1762
  " 2. 配置支持 Whisper 的 LLM API(自动使用,无需安装)\n"
1875
- " 3. pip install faster-whisper (离线本地,需 C++ 编译环境)\n"
1876
- " 4. pip install vosk (离线本地,需下载模型)\n"
1877
- " 5. pip install SpeechRecognition (需外网,国内不可用)",
1763
+ " 3. pip install vosk (离线本地,需下载模型)\n"
1764
+ " 4. pip install SpeechRecognition (需外网,国内不可用)",
1878
1765
  "available": False,
1879
1766
  }, status=503)
1880
1767
 
@@ -6772,8 +6659,8 @@ window.toggleFullscreen = function() {{
6772
6659
  except Exception:
6773
6660
  pass
6774
6661
 
6775
- # [v1.18.8] 后台预加载 STT 模型,避免首次语音识别时等待数秒
6776
- # 优先加载 SenseVoice(中文识别最佳),失败时回退到 faster-whisper
6662
+ # 后台预加载 STT 模型,避免首次语音识别时等待数秒
6663
+ # 仅加载 SenseVoice(中文识别最佳)
6777
6664
  try:
6778
6665
  import threading
6779
6666
  def _preload_stt():
@@ -6784,30 +6671,16 @@ window.toggleFullscreen = function() {{
6784
6671
  os.environ.setdefault("HF_HUB_DISABLE_PROGRESS_BARS", "1")
6785
6672
  os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
6786
6673
 
6787
- # 首选: SenseVoice (funasr)
6788
6674
  try:
6789
6675
  from funasr import AutoModel
6790
6676
  model_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'models', 'sensevoice')
6791
6677
  self._sensevoice_model = AutoModel(model="iic/SenseVoiceSmall", model_dir=model_dir,
6792
6678
  device="cpu", disable_pbar=True, disable_update=True)
6793
- logger.info("STT SenseVoice 模型预加载完成 (首选引擎)")
6794
- return # 成功则不加载 whisper
6795
- except ImportError:
6796
- logger.debug("SenseVoice (funasr) 未安装,尝试 faster-whisper")
6797
- except Exception as e:
6798
- logger.debug(f"SenseVoice 预加载失败: {e},尝试 faster-whisper")
6799
-
6800
- # 备选: faster-whisper
6801
- try:
6802
- from faster_whisper import WhisperModel
6803
- model_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'models', 'whisper')
6804
- self._whisper_model = WhisperModel("tiny", device="cpu", compute_type="int8",
6805
- download_root=model_dir)
6806
- logger.info("STT faster-whisper 模型预加载完成 (备选引擎)")
6679
+ logger.info("STT SenseVoice 模型预加载完成")
6807
6680
  except ImportError:
6808
- logger.debug("faster-whisper 未安装,跳过 STT 预加载")
6681
+ logger.debug("SenseVoice (funasr) 未安装,跳过 STT 预加载")
6809
6682
  except Exception as e:
6810
- logger.debug(f"STT 模型预加载失败(不影响使用): {e}")
6683
+ logger.debug(f"SenseVoice 预加载失败(不影响使用): {e}")
6811
6684
  except Exception as e:
6812
6685
  logger.debug(f"STT 预加载异常(不影响使用): {e}")
6813
6686
  threading.Thread(target=_preload_stt, daemon=True).start()
@@ -975,6 +975,7 @@ input,textarea,select{font:inherit}
975
975
  .toast-error{background:#fef2f2;color:#991b1b;border:1px solid #fecaca}
976
976
  .toast-success{background:#ecfdf5;color:#065f46;border:1px solid #a7f3d0}
977
977
  .toast-info{background:#eff6ff;color:#1e3a5f;border:1px solid #bfdbfe}
978
+ .toast-warning{background:#fffbeb;color:#92400e;border:1px solid #fde68a}
978
979
 
979
980
  /* ── Confirm Dialog ── */
980
981
  .modal-overlay{
@@ -2045,6 +2046,7 @@ input,textarea,select{font:inherit}
2045
2046
  [data-theme="dark"] .toast-error{background:rgba(127,29,29,.8);color:#fca5a5;border-color:rgba(239,68,68,.4)}
2046
2047
  [data-theme="dark"] .toast-success{background:rgba(6,78,59,.8);color:#6ee7b7;border-color:rgba(16,185,129,.4)}
2047
2048
  [data-theme="dark"] .toast-info{background:rgba(30,64,175,.8);color:#93c5fd;border-color:rgba(59,130,246,.4)}
2049
+ [data-theme="dark"] .toast-warning{background:rgba(113,63,18,.8);color:#fcd34d;border-color:rgba(245,158,11,.4)}
2048
2050
  [data-theme="dark"] .modal-overlay{background:rgba(0,0,0,.5)}
2049
2051
  [data-theme="dark"] .exec-badge.local{background:rgba(16,185,129,.15);color:#6ee7b7}
2050
2052
  [data-theme="dark"] .exec-badge.sandbox{background:rgba(245,158,11,.15);color:#fcd34d}
@@ -1871,6 +1871,20 @@ async function sendMessage(opts) {
1871
1871
  }
1872
1872
  ttsManager.streamDelta(evt.content);
1873
1873
  }
1874
+ } else if (evt.type === 'v2_iter_limit') {
1875
+ // 达到最大迭代次数限制
1876
+ flushV2Reasoning();
1877
+ flushCurrentText();
1878
+ // 弹窗通知用户
1879
+ if (typeof toast === 'function') {
1880
+ toast(evt.message || '任务已达到最大迭代次数,已保存为未完成任务', 'warning', 8000);
1881
+ }
1882
+ // 在消息中追加提示
1883
+ var _limitNotice = '\n\n> ⚠️ **达到迭代上限** (' + (evt.iterations || 0) + '/' + (evt.max_iterations || 0) + '),任务已自动保存为未完成任务。';
1884
+ if (state.messages[msgIdx]) {
1885
+ state.messages[msgIdx].content = (state.messages[msgIdx].content || '') + _limitNotice;
1886
+ }
1887
+ throttledStreamUpdate(msgIdx);
1874
1888
  } else if (evt.type === 'done') {
1875
1889
  // Flush remaining V2 reasoning as final text part
1876
1890
  flushV2Reasoning();