myagent-ai 1.16.17 → 1.16.19
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/Dockerfile +25 -1
- package/agents/main_agent.py +22 -0
- package/core/deps_checker.py +22 -1
- package/data/uploads/2026-04/32c9e9d3-8cf_test.pdf.pdf +1 -0
- package/executor/engine.py +97 -18
- package/install/install.sh +22 -0
- package/package.json +1 -1
- package/requirements.txt +21 -0
- package/skills/chromedev_mcp.py +179 -77
- package/skills/file_send.py +97 -0
- package/skills/file_skill.py +357 -35
- package/start.js +34 -0
- package/web/api_server.py +318 -37
- package/web/ui/chat/chat.css +21 -2
- package/web/ui/chat/chat_main.js +114 -13
- package/web/ui/chat/flow_engine.js +20 -0
- package/web/ui/index.html +23 -3
- package/worklog.md +26 -0
package/Dockerfile
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
# MyAgent - 本地桌面端执行型 AI 助手
|
|
2
2
|
# Docker 构建文件 (主要用于服务器部署)
|
|
3
|
-
FROM python:3.
|
|
3
|
+
FROM python:3.13-slim
|
|
4
4
|
|
|
5
5
|
WORKDIR /app
|
|
6
6
|
|
|
7
7
|
# 安装系统依赖
|
|
8
|
+
# [v1.16.18] 包含 poppler-utils (PDF提取)、Chrome 共享库 (浏览器自动化)
|
|
8
9
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
9
10
|
python3-pip \
|
|
11
|
+
poppler-utils \
|
|
12
|
+
# Chrome/Chromium 运行所需的共享库
|
|
13
|
+
libnss3 \
|
|
14
|
+
libatk1.0-0 \
|
|
15
|
+
libatk-bridge2.0-0 \
|
|
16
|
+
libcups2 \
|
|
17
|
+
libdrm2 \
|
|
18
|
+
libxkbcommon0 \
|
|
19
|
+
libxcomposite1 \
|
|
20
|
+
libxdamage1 \
|
|
21
|
+
libxfixes3 \
|
|
22
|
+
libxrandr2 \
|
|
23
|
+
libgbm1 \
|
|
24
|
+
libpango-1.0-0 \
|
|
25
|
+
libcairo2 \
|
|
26
|
+
libasound2 \
|
|
27
|
+
libatspi2.0-0 \
|
|
28
|
+
# Node.js (浏览器自动化 MCP 需要)
|
|
29
|
+
nodejs \
|
|
30
|
+
npm \
|
|
10
31
|
&& rm -rf /var/lib/apt/lists/*
|
|
11
32
|
|
|
33
|
+
# 安装 Puppeteer 的 Chrome (自包含,容器环境最可靠)
|
|
34
|
+
RUN npx -y puppeteer browsers install chrome@stable || true
|
|
35
|
+
|
|
12
36
|
# 安装 Python 依赖
|
|
13
37
|
COPY requirements.txt .
|
|
14
38
|
RUN pip install --no-cache-dir -r requirements.txt
|
package/agents/main_agent.py
CHANGED
|
@@ -73,6 +73,7 @@ class MainAgent(BaseAgent):
|
|
|
73
73
|
- **执行代码**: 用 `code` 工具(language: python/javascript/shell)
|
|
74
74
|
- **执行命令**: 用 `command` 或 `command_run` 工具
|
|
75
75
|
- **文件操作**: 用 `file_read` / `file_write` / `file_list` 等文件工具
|
|
76
|
+
- **发送文件给用户**: 用 `file_send` 工具(参数: file_path=文件路径, description=说明),当你生成或处理了文件需要返回给用户时使用
|
|
76
77
|
- **主动召回记忆**: 用 `recall_memory` 工具(参数: keyword=关键字, time_point=可选时间点如"2025-01", limit=数量默认5),根据关键字和时间搜索历史记忆
|
|
77
78
|
"""
|
|
78
79
|
|
|
@@ -552,11 +553,18 @@ class MainAgent(BaseAgent):
|
|
|
552
553
|
|
|
553
554
|
# 保存用户消息到会话记忆
|
|
554
555
|
if self.memory:
|
|
556
|
+
# [v1.16.17] 附加附件元数据到用户消息
|
|
557
|
+
_attachment_meta = {}
|
|
558
|
+
if context.metadata.get("user_image_files"):
|
|
559
|
+
_attachment_meta["images"] = context.metadata["user_image_files"]
|
|
560
|
+
if context.metadata.get("user_file_files"):
|
|
561
|
+
_attachment_meta["files"] = context.metadata["user_file_files"]
|
|
555
562
|
self.memory.add_session(
|
|
556
563
|
session_id=context.session_id,
|
|
557
564
|
role="user",
|
|
558
565
|
content=context.user_message,
|
|
559
566
|
key="user_input",
|
|
567
|
+
metadata=_attachment_meta if _attachment_meta else None,
|
|
560
568
|
)
|
|
561
569
|
|
|
562
570
|
# 加载相关记忆 (recall from previous round or initial load)
|
|
@@ -1561,6 +1569,20 @@ class MainAgent(BaseAgent):
|
|
|
1561
1569
|
result = {"success": False, "error": f"记忆召回失败: {re_err}"}
|
|
1562
1570
|
logger.warning(f"[{task_id}] recall_memory 工具异常: {re_err}")
|
|
1563
1571
|
|
|
1572
|
+
elif tool_name == "file_send":
|
|
1573
|
+
# [v1.16.17→18] 文件发送工具 — 让 Agent 向用户发送文件
|
|
1574
|
+
try:
|
|
1575
|
+
from skills.file_send import FileSendSkill
|
|
1576
|
+
_fskill = FileSendSkill()
|
|
1577
|
+
_fpath = params.get("file_path", "")
|
|
1578
|
+
_fdesc = params.get("description", "")
|
|
1579
|
+
# [v1.16.18] 使用当前作用域的 stream_callback(而非 context._stream_callback)
|
|
1580
|
+
_fresult = await _fskill.execute(_fpath, _fdesc, stream_callback=stream_callback)
|
|
1581
|
+
result = {"success": True, "output": json.dumps(_fresult, ensure_ascii=False, indent=2), "data": _fresult}
|
|
1582
|
+
except Exception as _fse:
|
|
1583
|
+
result = {"success": False, "error": f"文件发送失败: {_fse}"}
|
|
1584
|
+
logger.warning(f"[{task_id}] file_send 工具异常: {_fse}")
|
|
1585
|
+
|
|
1564
1586
|
elif self.skills:
|
|
1565
1587
|
exec_result = await self.skills.execute(tool_name, **params)
|
|
1566
1588
|
if exec_result is None:
|
package/core/deps_checker.py
CHANGED
|
@@ -13,7 +13,8 @@ core/deps_checker.py - 自动依赖检测与安装
|
|
|
13
13
|
依赖映射:
|
|
14
14
|
核心功能: openai, aiohttp, requests
|
|
15
15
|
搜索技能: bs4
|
|
16
|
-
|
|
16
|
+
PDF 处理: PyPDF2
|
|
17
|
+
系统技能: psutil, chardet
|
|
17
18
|
托盘功能: pystray, PIL
|
|
18
19
|
语音合成: edge_tts
|
|
19
20
|
浏览器自动化: chrome-devtools-mcp (Node.js, Chrome DevTools Protocol)
|
|
@@ -64,6 +65,24 @@ DEPENDENCIES: List[DepInfo] = [
|
|
|
64
65
|
# ── 搜索技能 ──
|
|
65
66
|
DepInfo("bs4", "beautifulsoup4", "4.12.0", "search", "all"),
|
|
66
67
|
|
|
68
|
+
# ── PDF 处理 ──
|
|
69
|
+
DepInfo("PyPDF2", "PyPDF2", "3.0.0", "pdf", "all",
|
|
70
|
+
note="纯 Python PDF 文本提取(pdftotext 失败时的备用方案)"),
|
|
71
|
+
|
|
72
|
+
# ── 文档处理 (Excel/Word/PPT) ──
|
|
73
|
+
DepInfo("openpyxl", "openpyxl", "3.1.0", "doc", "all",
|
|
74
|
+
note="Excel (.xlsx) 文件读取"),
|
|
75
|
+
DepInfo("docx", "python-docx", "1.1.0", "doc", "all",
|
|
76
|
+
note="Word (.docx) 文件读取"),
|
|
77
|
+
DepInfo("pptx", "python-pptx", "0.6.21", "doc", "all",
|
|
78
|
+
note="PowerPoint (.pptx) 文件读取"),
|
|
79
|
+
DepInfo("xlrd", "xlrd", "2.0.0", "doc", "all",
|
|
80
|
+
note="旧版 Excel (.xls) 文件读取"),
|
|
81
|
+
|
|
82
|
+
# ── 文件编码检测 ──
|
|
83
|
+
DepInfo("chardet", "chardet", "5.0.0", "system", "all",
|
|
84
|
+
note="文件编码自动检测"),
|
|
85
|
+
|
|
67
86
|
# ── 系统技能 ──
|
|
68
87
|
DepInfo("psutil", "psutil", "5.9.0", "system", "all"),
|
|
69
88
|
|
|
@@ -404,6 +423,8 @@ def ensure_skill_deps(skill_category: str) -> bool:
|
|
|
404
423
|
"tts": {"tts"},
|
|
405
424
|
"stt": {"stt"},
|
|
406
425
|
"tray": {"tray"},
|
|
426
|
+
"pdf": {"pdf"},
|
|
427
|
+
"doc": {"doc"},
|
|
407
428
|
}
|
|
408
429
|
|
|
409
430
|
cats = category_map.get(skill_category, {skill_category})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
%PDF-1.4 test content
|
package/executor/engine.py
CHANGED
|
@@ -231,11 +231,16 @@ class ExecutionEngine:
|
|
|
231
231
|
|
|
232
232
|
# 沙盒模式: 检查 Docker 可用性
|
|
233
233
|
self._docker_available = False
|
|
234
|
+
self._docker_warned = False # [v1.16.19] 标记是否已警告过 Docker 不可用
|
|
234
235
|
if self.execution_mode == "sandbox":
|
|
235
236
|
self._docker_available = self._check_docker()
|
|
236
237
|
if not self._docker_available:
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
# [v1.16.19] Docker 不可用时,使用轻量级进程沙盒(而非回退到本机模式)
|
|
239
|
+
# 轻量级沙盒: 临时工作目录 + subprocess 隔离,无全局锁,支持多 agent 并发
|
|
240
|
+
logger.info("沙盒模式: Docker 不可用,使用轻量级进程沙盒(临时工作目录隔离)")
|
|
241
|
+
self._docker_warned = True
|
|
242
|
+
# 不再修改 self.execution_mode,保持 "sandbox"
|
|
243
|
+
# 轻量级沙盒仍然标记为 sandbox 模式,不走全局执行锁
|
|
239
244
|
|
|
240
245
|
# ── 启动时缓存: 检测平台 & Shell ────────────────────────────────────
|
|
241
246
|
self._platform = detect_platform()
|
|
@@ -525,13 +530,17 @@ class ExecutionEngine:
|
|
|
525
530
|
self._execution_count += 1
|
|
526
531
|
exec_id = f"exec_{self._execution_count}"
|
|
527
532
|
|
|
528
|
-
mode_label = "本地" if self.execution_mode == "local" else "沙盒
|
|
533
|
+
mode_label = "本地" if self.execution_mode == "local" else ("沙盒" if self._docker_available else "沙盒")
|
|
529
534
|
logger.info(f"[{exec_id}] 开始执行 ({language}, mode={mode_label}, timeout={exec_timeout}s)")
|
|
530
535
|
logger.debug(f"[{exec_id}] 代码:\n{code[:500]}")
|
|
531
536
|
|
|
532
537
|
# 根据执行模式选择执行方式
|
|
533
|
-
if self.execution_mode == "sandbox"
|
|
534
|
-
|
|
538
|
+
if self.execution_mode == "sandbox":
|
|
539
|
+
if self._docker_available:
|
|
540
|
+
result = await self._execute_sandbox(language, code, exec_timeout, work_dir, env, exec_id)
|
|
541
|
+
else:
|
|
542
|
+
# [v1.16.19] Docker 不可用时,使用轻量级进程沙盒
|
|
543
|
+
result = await self._execute_lightweight_sandbox(language, code, exec_timeout, work_dir, env, exec_id)
|
|
535
544
|
elif language == "python":
|
|
536
545
|
result = await self._execute_python(code, exec_timeout, work_dir, env, exec_id)
|
|
537
546
|
else:
|
|
@@ -539,7 +548,11 @@ class ExecutionEngine:
|
|
|
539
548
|
|
|
540
549
|
result.language = language
|
|
541
550
|
result.code = code
|
|
542
|
-
|
|
551
|
+
# [v1.16.19] 合并 metadata,不覆盖执行模式等内部字段
|
|
552
|
+
if result.metadata:
|
|
553
|
+
result.metadata.update(metadata)
|
|
554
|
+
else:
|
|
555
|
+
result.metadata = metadata
|
|
543
556
|
|
|
544
557
|
if result.success:
|
|
545
558
|
logger.info(f"[{exec_id}] 执行成功 (耗时: {result.execution_time:.2f}s)")
|
|
@@ -823,6 +836,65 @@ class ExecutionEngine:
|
|
|
823
836
|
execution_time=elapsed,
|
|
824
837
|
)
|
|
825
838
|
|
|
839
|
+
# ======================================================================
|
|
840
|
+
# [v1.16.19] 轻量级进程沙盒(Docker 不可用时的替代方案)
|
|
841
|
+
# ======================================================================
|
|
842
|
+
# 设计目标:
|
|
843
|
+
# - 使用 agent 自身的固定工作目录(不是临时目录)
|
|
844
|
+
# - 不使用全局执行锁,多 agent 可并发
|
|
845
|
+
# - 与本机模式的唯一区别:不走全局锁,允许多 agent 同时执行
|
|
846
|
+
# - 有 Docker 时用 Docker 容器提供真正隔离,无 Docker 时直接在
|
|
847
|
+
# agent 工作目录执行(各 agent 有自己的工作目录,天然隔离)
|
|
848
|
+
# ======================================================================
|
|
849
|
+
|
|
850
|
+
async def _execute_lightweight_sandbox(
|
|
851
|
+
self,
|
|
852
|
+
language: str,
|
|
853
|
+
code: str,
|
|
854
|
+
timeout: int,
|
|
855
|
+
work_dir: str,
|
|
856
|
+
env: Optional[Dict[str, str]],
|
|
857
|
+
exec_id: str,
|
|
858
|
+
) -> ExecResult:
|
|
859
|
+
"""直接在 agent 工作目录中执行代码(轻量级沙盒,无需 Docker)。
|
|
860
|
+
|
|
861
|
+
与本机模式的区别:
|
|
862
|
+
- 不使用全局执行锁,多 agent 可并发执行
|
|
863
|
+
- 每个 agent 使用自己的固定工作目录,天然隔离
|
|
864
|
+
- 执行方式与本机模式完全相同(subprocess)
|
|
865
|
+
"""
|
|
866
|
+
start_time = asyncio.get_event_loop().time()
|
|
867
|
+
|
|
868
|
+
try:
|
|
869
|
+
# 直接在 agent 工作目录中执行(不创建临时目录)
|
|
870
|
+
if language == "python":
|
|
871
|
+
result = await self._execute_python(code, timeout, work_dir, env, exec_id)
|
|
872
|
+
elif language in ("powershell",):
|
|
873
|
+
result = ExecResult(
|
|
874
|
+
success=False,
|
|
875
|
+
error="PowerShell 不支持沙盒模式",
|
|
876
|
+
execution_time=asyncio.get_event_loop().time() - start_time,
|
|
877
|
+
metadata={"mode": "sandbox"},
|
|
878
|
+
)
|
|
879
|
+
else:
|
|
880
|
+
result = await self._execute_shell(language, code, timeout, work_dir, env, exec_id)
|
|
881
|
+
|
|
882
|
+
# 标记为沙盒模式
|
|
883
|
+
if result.metadata is None:
|
|
884
|
+
result.metadata = {}
|
|
885
|
+
result.metadata["mode"] = "sandbox"
|
|
886
|
+
|
|
887
|
+
return result
|
|
888
|
+
|
|
889
|
+
except Exception as e:
|
|
890
|
+
elapsed = asyncio.get_event_loop().time() - start_time
|
|
891
|
+
return ExecResult(
|
|
892
|
+
success=False,
|
|
893
|
+
error=f"沙盒执行异常: {e}",
|
|
894
|
+
execution_time=elapsed,
|
|
895
|
+
metadata={"mode": "sandbox"},
|
|
896
|
+
)
|
|
897
|
+
|
|
826
898
|
# ======================================================================
|
|
827
899
|
# 沙盒执行 (Docker 容器)
|
|
828
900
|
# ======================================================================
|
|
@@ -902,13 +974,12 @@ class ExecutionEngine:
|
|
|
902
974
|
metadata={"mode": "sandbox"},
|
|
903
975
|
)
|
|
904
976
|
except FileNotFoundError:
|
|
905
|
-
# Docker
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return await self._execute_shell(language, code, timeout, work_dir, env, exec_id)
|
|
977
|
+
# Docker 不存在,回退到轻量级沙盒
|
|
978
|
+
if not self._docker_warned:
|
|
979
|
+
logger.info(f"[{exec_id}] Docker 不可用,使用轻量级沙盒")
|
|
980
|
+
self._docker_warned = True
|
|
981
|
+
self._docker_available = False
|
|
982
|
+
return await self._execute_lightweight_sandbox(language, code, timeout, work_dir, env, exec_id)
|
|
912
983
|
except Exception as e:
|
|
913
984
|
elapsed = asyncio.get_event_loop().time() - start_time
|
|
914
985
|
return ExecResult(
|
|
@@ -923,19 +994,27 @@ class ExecutionEngine:
|
|
|
923
994
|
if mode not in ("local", "sandbox"):
|
|
924
995
|
return False
|
|
925
996
|
if mode == "sandbox":
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
997
|
+
self._docker_available = self._check_docker()
|
|
998
|
+
if not self._docker_available:
|
|
999
|
+
# [v1.16.19] Docker 不可用时,使用轻量级进程沙盒
|
|
1000
|
+
# 不再回退到本机模式,保持 sandbox 避免全局锁
|
|
1001
|
+
if not self._docker_warned:
|
|
1002
|
+
logger.info("沙盒模式: Docker 不可用,使用轻量级进程沙盒(临时工作目录隔离,无全局锁)")
|
|
1003
|
+
self._docker_warned = True
|
|
1004
|
+
# 不 return False,允许切换到 sandbox
|
|
1005
|
+
else:
|
|
1006
|
+
self._docker_available = True
|
|
930
1007
|
self.execution_mode = mode
|
|
931
|
-
logger.info(f"执行模式已切换: {mode}")
|
|
1008
|
+
logger.info(f"执行模式已切换: {mode}" + (" (Docker)" if mode == "sandbox" and self._docker_available else " (轻量级进程沙盒)" if mode == "sandbox" else ""))
|
|
932
1009
|
return True
|
|
933
1010
|
|
|
934
1011
|
def get_execution_info(self) -> Dict[str, Any]:
|
|
935
1012
|
"""获取当前执行模式信息。"""
|
|
1013
|
+
sandbox_type = "docker" if self._docker_available else ("lightweight" if self.execution_mode == "sandbox" else None)
|
|
936
1014
|
return {
|
|
937
1015
|
"mode": self.execution_mode,
|
|
938
1016
|
"docker_available": self._docker_available,
|
|
1017
|
+
"sandbox_type": sandbox_type, # [v1.16.19] "docker" | "lightweight" | None
|
|
939
1018
|
"sandbox_image": self.sandbox_image,
|
|
940
1019
|
"sandbox_network": self.sandbox_network,
|
|
941
1020
|
"sandbox_memory": self.sandbox_memory,
|
package/install/install.sh
CHANGED
|
@@ -171,6 +171,27 @@ install_node() {
|
|
|
171
171
|
exit 1
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
# ── Linux 系统依赖 ──────────────────────────────────────────
|
|
175
|
+
install_linux_system_deps() {
|
|
176
|
+
if [ "$OS" != "linux" ] || ! command -v apt-get &>/dev/null; then return; fi
|
|
177
|
+
step "Installing system dependencies (poppler-utils, Chrome libs) ..."
|
|
178
|
+
sudo apt-get update -qq 2>/dev/null || true
|
|
179
|
+
# poppler-utils: PDF 文本提取 (pdftotext)
|
|
180
|
+
# Chrome/Chromium 共享库: 浏览器自动化必需
|
|
181
|
+
sudo apt-get install -y --no-install-recommends \
|
|
182
|
+
poppler-utils \
|
|
183
|
+
libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
|
|
184
|
+
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
|
|
185
|
+
libgbm1 libpango-1.0-0 libcairo2 libasound2 libatspi2.0-0 \
|
|
186
|
+
2>/dev/null || true
|
|
187
|
+
# 尝试安装 Chromium (如果尚未安装)
|
|
188
|
+
if ! command -v google-chrome &>/dev/null && ! command -v chromium &>/dev/null && ! command -v chromium-browser &>/dev/null; then
|
|
189
|
+
sudo apt-get install -y --no-install-recommends chromium-browser 2>/dev/null || \
|
|
190
|
+
sudo apt-get install -y --no-install-recommends chromium 2>/dev/null || true
|
|
191
|
+
fi
|
|
192
|
+
success "系统依赖安装完成"
|
|
193
|
+
}
|
|
194
|
+
|
|
174
195
|
# ── 找到 myagent-ai 命令路径 ──────────────────────────────
|
|
175
196
|
find_myagent_cmd() {
|
|
176
197
|
if command -v myagent-ai &>/dev/null; then
|
|
@@ -192,6 +213,7 @@ find_myagent_cmd() {
|
|
|
192
213
|
if ! $NO_DEPS; then
|
|
193
214
|
check_python || install_python
|
|
194
215
|
check_node || install_node
|
|
216
|
+
install_linux_system_deps
|
|
195
217
|
fi
|
|
196
218
|
|
|
197
219
|
step "Installing $PKG_NAME via npm ..."
|
package/package.json
CHANGED
package/requirements.txt
CHANGED
|
@@ -14,6 +14,19 @@ requests>=2.31.0
|
|
|
14
14
|
beautifulsoup4>=4.12.0
|
|
15
15
|
psutil>=5.9.0
|
|
16
16
|
|
|
17
|
+
# ============================================================
|
|
18
|
+
# 技能系统 - 文档处理 (PDF/Excel/Word/PPT)
|
|
19
|
+
# ============================================================
|
|
20
|
+
# PyPDF2: 纯 Python PDF 文本提取(无需系统依赖,保证开箱即用)
|
|
21
|
+
# 系统有 poppler-utils 时优先使用 pdftotext(更快更准),无时自动 fallback
|
|
22
|
+
PyPDF2>=3.0.0
|
|
23
|
+
# openpyxl: Excel (.xlsx) 文件读取(上传 Excel 自动提取内容)
|
|
24
|
+
openpyxl>=3.1.0
|
|
25
|
+
# python-docx: Word (.docx) 文件读取
|
|
26
|
+
python-docx>=1.1.0
|
|
27
|
+
# python-pptx: PowerPoint (.pptx) 文件读取
|
|
28
|
+
python-pptx>=0.6.21
|
|
29
|
+
|
|
17
30
|
# ============================================================
|
|
18
31
|
# 技能系统 - 浏览器自动化 (ChromeDev MCP)
|
|
19
32
|
# ============================================================
|
|
@@ -48,6 +61,14 @@ discord.py>=2.3.0
|
|
|
48
61
|
# ============================================================
|
|
49
62
|
edge-tts>=6.1.0
|
|
50
63
|
|
|
64
|
+
# ============================================================
|
|
65
|
+
# 系统工具增强 (预装,避免运行时缺失)
|
|
66
|
+
# ============================================================
|
|
67
|
+
# chardet: 文件编码检测(上传文件时自动识别编码)
|
|
68
|
+
chardet>=5.0.0
|
|
69
|
+
# xlrd: 旧版 Excel (.xls) 文件读取
|
|
70
|
+
xlrd>=2.0.0
|
|
71
|
+
|
|
51
72
|
# ============================================================
|
|
52
73
|
# 语音识别 (本地 STT,默认启用)
|
|
53
74
|
# ============================================================
|