myagent-ai 1.18.7 → 1.18.8

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.
@@ -100,8 +100,10 @@ DEPENDENCIES: List[DepInfo] = [
100
100
  DepInfo("edge_tts", "edge-tts", "6.1.0", "tts", "all"),
101
101
 
102
102
  # ── 语音识别 (STT) ──
103
+ DepInfo("funasr", "funasr", "1.1.0", "stt", "all",
104
+ note="[v1.18.7] SenseVoice 中文语音识别(推荐,需 torch+torchaudio)"),
103
105
  DepInfo("faster_whisper", "faster-whisper", "1.0.0", "stt", "all",
104
- note="本地语音识别引擎 (需要 C++ 编译)"),
106
+ note="Whisper 本地语音识别引擎 (需要 C++ 编译)"),
105
107
  DepInfo("speech_recognition", "SpeechRecognition", "3.10.0", "stt", "all",
106
108
  note="在线语音识别 (Google API,纯 Python 无需编译,Termux 兼容)"),
107
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.18.7",
3
+ "version": "1.18.8",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/skills/base.py CHANGED
@@ -94,10 +94,14 @@ class Skill(ABC):
94
94
  pass
95
95
 
96
96
  def validate_params(self, params: Dict[str, Any]) -> tuple[bool, str]:
97
- """校验参数是否合法"""
97
+ """校验参数是否合法。有默认值的必需参数在缺失时自动填充。"""
98
98
  for p in self.parameters:
99
- if p.required and p.name not in params:
100
- return False, f"缺少必需参数: {p.name}"
99
+ if p.name not in params:
100
+ # [v1.18.7] 有默认值的必需参数:自动填充而非报错
101
+ if p.default is not None:
102
+ params[p.name] = p.default
103
+ elif p.required:
104
+ return False, f"缺少必需参数: {p.name}"
101
105
  if p.name in params and p.enum and params[p.name] not in p.enum:
102
106
  return False, f"参数 {p.name} 值无效,可选: {p.enum}"
103
107
  return True, ""
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import json
10
10
  import os
11
+ import time
11
12
  from pathlib import Path
12
13
  from typing import Any, Dict, List, Optional
13
14
 
@@ -29,18 +30,19 @@ class XLSXCreateSkill(Skill):
29
30
  """
30
31
  name = "xlsx_create"
31
32
  description = (
32
- "生成 Excel (XLSX) 电子表格。支持多工作表、表头、数据行、公式、"
33
+ "生成 Excel (XLSX) 电子表格文件。支持多工作表、表头、数据行、公式、"
33
34
  "冻结窗格、自动筛选、列宽设置。"
34
- "sheets JSON 对象,key 为工作表名。"
35
+ "参数 sheets 必须是 JSON 字符串,格式为 {\"工作表名\": {\"headers\":[...], \"rows\":[[...]]}}。"
36
+ "参数 output_path 指定输出文件路径,默认为工作目录下。"
35
37
  )
36
38
  category = "doc"
37
39
  dangerous = True
38
40
  parameters = [
39
41
  SkillParameter("sheets", "string",
40
- "工作表数据 JSON 对象。key=工作表名, value={headers, rows, col_widths?, formulas?}。"
41
- "示例: {\"Sheet1\":{\"headers\":[\"Name\",\"Score\"],\"rows\":[[\"Alice\",95]]}}",
42
+ "工作表数据,JSON 字符串。key=工作表名, value包含 headers(列名数组) rows(数据行二维数组)。"
43
+ "示例: '{\"Sheet1\":{\"headers\":[\"姓名\",\"分数\"],\"rows\":[[\"Alice\",95]]}}'",
42
44
  required=True),
43
- SkillParameter("output_path", "string", "输出 XLSX 文件路径", required=True),
45
+ SkillParameter("output_path", "string", "输出文件路径(如 /tmp/report.xlsx)", required=False, default=""),
44
46
  SkillParameter("title", "string", "文档标题", required=False, default=""),
45
47
  ]
46
48
 
@@ -62,6 +64,15 @@ class XLSXCreateSkill(Skill):
62
64
 
63
65
  try:
64
66
  out = Path(output_path).expanduser().resolve()
67
+ if not output_path.strip():
68
+ # [v1.18.7] 默认输出路径:工作目录/data/workspace下
69
+ from core.context_manager import get_active_context
70
+ try:
71
+ ctx = get_active_context()
72
+ work_dir = Path(ctx.work_dir) if ctx and ctx.work_dir else Path.cwd()
73
+ except Exception:
74
+ work_dir = Path.cwd()
75
+ out = work_dir / f"report_{int(time.time())}.xlsx"
65
76
  out.parent.mkdir(parents=True, exist_ok=True)
66
77
 
67
78
  wb = openpyxl.Workbook()
package/web/api_server.py CHANGED
@@ -1545,13 +1545,15 @@ window.toggleFullscreen = function() {{
1545
1545
  return web.json_response({"error": str(e)}, status=500)
1546
1546
 
1547
1547
  async def handle_voice_stt(self, request):
1548
- """POST /api/voice-stt - 轻量级本地语音转文字
1548
+ """POST /api/voice-stt - 本地语音转文字
1549
1549
 
1550
1550
  接受音频文件(WAV/WEBM/OGG),使用本地 STT 引擎转录。
1551
1551
  支持的引擎(按优先级):
1552
- 1. faster-whisper(推荐,需安装:pip install faster-whisper
1553
- 2. vosk(备选,需安装:pip install vosk
1554
- 如果都未安装,返回错误提示。
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,需外网)
1555
1557
  """
1556
1558
  try:
1557
1559
  reader = await request.multipart()
@@ -1584,6 +1586,59 @@ window.toggleFullscreen = function() {{
1584
1586
 
1585
1587
  import io
1586
1588
 
1589
+ # ── [v1.18.7] 首选: SenseVoice(阿里达摩院,中文识别极佳) ──
1590
+ try:
1591
+ sv_model = getattr(self, '_sensevoice_model', None)
1592
+ if sv_model is None:
1593
+ os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1")
1594
+ os.environ.setdefault("HF_HUB_DISABLE_PROGRESS_BARS", "1")
1595
+ from funasr import AutoModel
1596
+ model_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'models', 'sensevoice')
1597
+ sv_model = AutoModel(model="iic/SenseVoiceSmall", model_dir=model_dir,
1598
+ device="cpu", disable_pbar=True, disable_update=True)
1599
+ self._sensevoice_model = sv_model
1600
+ logger.info("SenseVoice 模型已加载 (iic/SenseVoiceSmall, CPU)")
1601
+
1602
+ # SenseVoice 接受 16kHz WAV
1603
+ wav_path = f"/tmp/myagent_stt_{id(audio_data) % 100000}.wav"
1604
+ wav_buf = io.BytesIO()
1605
+ try:
1606
+ from pydub import AudioSegment
1607
+ audio_buf = io.BytesIO(audio_data)
1608
+ seg = AudioSegment.from_file(audio_buf, format=audio_format or "webm")
1609
+ seg = seg.set_channels(1).set_frame_rate(16000).set_sample_width(2)
1610
+ seg.export(wav_buf, format="wav")
1611
+ except Exception:
1612
+ wav_buf = io.BytesIO(audio_data)
1613
+ wav_buf.seek(0)
1614
+ with open(wav_path, 'wb') as f:
1615
+ f.write(wav_buf.read())
1616
+
1617
+ # SenseVoice 推理
1618
+ res = sv_model.generate(input=wav_path, cache={},
1619
+ language="auto", # 自动检测语言
1620
+ use_itn=True, # 逆文本标准化(数字/日期等)
1621
+ batch_size_s=300)
1622
+ if res and len(res) > 0 and len(res[0]) > 0:
1623
+ text = res[0][0]["text"] if isinstance(res[0][0], dict) else str(res[0][0])
1624
+ # SenseVoice 可能输出带 <|zh|><|en|><|EMO|> 等特殊 token,清理掉
1625
+ import re
1626
+ text = re.sub(r'<\|[^|]+\|>', '', text).strip()
1627
+ if text:
1628
+ try:
1629
+ os.remove(wav_path)
1630
+ except Exception:
1631
+ pass
1632
+ return web.json_response({"text": text, "engine": "sensevoice"})
1633
+ try:
1634
+ os.remove(wav_path)
1635
+ except Exception:
1636
+ pass
1637
+ except ImportError:
1638
+ logger.debug("SenseVoice (funasr) 未安装,跳过。安装: pip install funasr torch torchaudio")
1639
+ except Exception as e:
1640
+ logger.warning(f"SenseVoice 转录失败: {e}")
1641
+
1587
1642
  # ── 尝试 faster-whisper ──
1588
1643
  try:
1589
1644
  whisper_model = self._whisper_model
@@ -1815,10 +1870,11 @@ window.toggleFullscreen = function() {{
1815
1870
  # ── 没有可用的 STT 引擎 ──
1816
1871
  return web.json_response({
1817
1872
  "error": "未检测到可用的 STT 引擎。请尝试以下方案:\n"
1818
- " 1. 配置支持 Whisper LLM API(自动使用,推荐)\n"
1819
- " 2. pip install faster-whisper (离线本地,需 C++ 编译环境)\n"
1820
- " 3. pip install vosk (离线本地,需下载模型)\n"
1821
- " 4. pip install SpeechRecognition (需外网,国内不可用)",
1873
+ " 1. pip install funasr torch torchaudio (SenseVoice,中文最佳,推荐)\n"
1874
+ " 2. 配置支持 Whisper LLM API(自动使用,无需安装)\n"
1875
+ " 3. pip install faster-whisper (离线本地,需 C++ 编译环境)\n"
1876
+ " 4. pip install vosk (离线本地,需下载模型)\n"
1877
+ " 5. pip install SpeechRecognition (需外网,国内不可用)",
1822
1878
  "available": False,
1823
1879
  }, status=503)
1824
1880