处理prompt_toolkit

This commit is contained in:
dark 2026-06-04 13:59:55 +08:00
parent badcce5d2d
commit 30c6532f23
5 changed files with 44 additions and 10 deletions

View File

@ -79,7 +79,7 @@ packaging/
- 本地已安装 `langgraph``mcp`,并完成 LangGraph fake 全局流程 smoke。 - 本地已安装 `langgraph``mcp`,并完成 LangGraph fake 全局流程 smoke。
- CLI `analyze` 输出已做敏感字段脱敏。 - CLI `analyze` 输出已做敏感字段脱敏。
- 增加 `chat` 常驻式 CLI 对话框支持自然语言分析、参数设置、执行确认、显式回滚、状态查看、事件查看、checkpoint 选择和续跑。 - 增加 `chat` 常驻式 CLI 对话框支持自然语言分析、参数设置、执行确认、显式回滚、状态查看、事件查看、checkpoint 选择和续跑。
- chat 在开发环境可选启用 `rich` / `prompt_toolkit`PyInstaller 打包环境默认使用普通文本输入,避免交互兼容问题 - chat 在开发环境和默认发布包中都会优先启用 `rich` / `prompt_toolkit`;如果增强输入初始化失败,会自动降级到普通 `input()`
- chat 执行前会归一化参数并展示实际写入脚本配置的值;`script_only` / `hybrid_node_mcp` 会提前检查 `ZIP_FILE_PATH` 是否存在。 - chat 执行前会归一化参数并展示实际写入脚本配置的值;`script_only` / `hybrid_node_mcp` 会提前检查 `ZIP_FILE_PATH` 是否存在。
- chat 执行中会播报每个 action 的开始、完成或失败action 执行失败会停在当前 checkpoint不再误报 LangGraph 不可用。 - chat 执行中会播报每个 action 的开始、完成或失败action 执行失败会停在当前 checkpoint不再误报 LangGraph 不可用。
- 每个 action 完成后都会进入一次 LLM/规则审核;如果审核建议停止,流程会暂停并给出建议,等待用户 `resume` - 每个 action 完成后都会进入一次 LLM/规则审核;如果审核建议停止,流程会暂停并给出建议,等待用户 `resume`

View File

@ -34,7 +34,7 @@ pam-deploy-agent-linux-x86_64/
./run.sh run-deploy --help ./run.sh run-deploy --help
``` ```
发布包默认使用普通文本输入,避免 PyInstaller 环境下 `prompt_toolkit` 兼容性问题;输出仍会在可用时使用 `rich` 做更清晰的文本展示。 发布包默认会优先使用 `prompt_toolkit` 增强输入,支持更稳定的退格、历史记录和补全;如果增强输入初始化失败,会自动降级到普通 `input()`输出仍会在可用时使用 `rich` 做更清晰的文本展示。
逐 IP action 失败后会保存 checkpoint 并暂停;修复外部环境后输入 `resume` 会从失败 action 重试。回滚不再属于主 workflow 自动分支,需要时在 chat 内输入 `rollback [IP]` 显式执行。 逐 IP action 失败后会保存 checkpoint 并暂停;修复外部环境后输入 `resume` 会从失败 action 重试。回滚不再属于主 workflow 自动分支,需要时在 chat 内输入 `rollback [IP]` 显式执行。
chat 会在执行前归一化并展示实际写入脚本配置的参数;`script_only` / `hybrid_node_mcp` 会先检查 `ZIP_FILE_PATH` 是否存在,避免脚本运行后才用默认路径失败。执行过程中每个 action 都会输出开始、完成或失败状态;每个 action 完成后还会自动进入一次 LLM/规则审核,并播报审核开始和审核结果。 chat 会在执行前归一化并展示实际写入脚本配置的参数;`script_only` / `hybrid_node_mcp` 会先检查 `ZIP_FILE_PATH` 是否存在,避免脚本运行后才用默认路径失败。执行过程中每个 action 都会输出开始、完成或失败状态;每个 action 完成后还会自动进入一次 LLM/规则审核,并播报审核开始和审核结果。

View File

@ -44,6 +44,14 @@ else
python -m pip install -e . python -m pip install -e .
fi fi
PYINSTALLER_EXTRA_ARGS=()
if python -c "import importlib.util; raise SystemExit(0 if importlib.util.find_spec('prompt_toolkit') else 1)"; then
PYINSTALLER_EXTRA_ARGS+=(--collect-submodules prompt_toolkit --collect-data prompt_toolkit)
fi
if python -c "import importlib.util; raise SystemExit(0 if importlib.util.find_spec('rich') else 1)"; then
PYINSTALLER_EXTRA_ARGS+=(--collect-submodules rich)
fi
echo "==> 使用 PyInstaller 生成自带 Python 运行时的可执行目录" echo "==> 使用 PyInstaller 生成自带 Python 运行时的可执行目录"
python -m PyInstaller \ python -m PyInstaller \
--clean \ --clean \
@ -57,6 +65,7 @@ python -m PyInstaller \
--collect-submodules pam_deploy_graph \ --collect-submodules pam_deploy_graph \
--collect-submodules langgraph \ --collect-submodules langgraph \
--hidden-import pam_deploy_graph.cli \ --hidden-import pam_deploy_graph.cli \
"${PYINSTALLER_EXTRA_ARGS[@]}" \
packaging/pyinstaller_entry.py packaging/pyinstaller_entry.py
echo "==> 组装发布目录" echo "==> 组装发布目录"

View File

@ -8,7 +8,6 @@ import shlex
import builtins import builtins
import logging import logging
import os import os
import sys
from dataclasses import asdict from dataclasses import asdict
from pathlib import Path from pathlib import Path
from typing import Any, Callable from typing import Any, Callable
@ -1058,14 +1057,12 @@ def _build_prompt_input(input_func: InputFunc) -> InputFunc:
"""如果安装了 prompt_toolkit则启用历史记录和命令补全。""" """如果安装了 prompt_toolkit则启用历史记录和命令补全。"""
if input_func is not builtins.input: if input_func is not builtins.input:
return input_func return input_func
if getattr(sys, "frozen", False):
return input_func
try: try:
from prompt_toolkit import PromptSession from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.history import FileHistory from prompt_toolkit.history import FileHistory
except ImportError: except ImportError:
return input_func return _build_readline_input(input_func)
commands = [ commands = [
"help", "help",
@ -1099,13 +1096,28 @@ def _build_prompt_input(input_func: InputFunc) -> InputFunc:
except OSError: except OSError:
history = None history = None
try:
session = PromptSession( session = PromptSession(
history=history, history=history,
completer=WordCompleter(commands, ignore_case=True, sentence=True), completer=WordCompleter(commands, ignore_case=True, sentence=True),
) )
except Exception:
logger.exception("chat prompt_toolkit 初始化失败,降级为普通 input")
return _build_readline_input(input_func)
return session.prompt return session.prompt
def _build_readline_input(input_func: InputFunc) -> InputFunc:
"""在没有 prompt_toolkit 时尽量启用 GNU readline改善 Linux 终端退格键兼容。"""
if input_func is not builtins.input or os.name == "nt":
return input_func
try:
import readline # noqa: F401
except ImportError:
logger.debug("chat readline 不可用,使用普通 input")
return input_func
def _build_output_func(output_func: OutputFunc) -> OutputFunc: def _build_output_func(output_func: OutputFunc) -> OutputFunc:
"""如果安装了 rich则使用 rich 输出;否则保持原输出函数。""" """如果安装了 rich则使用 rich 输出;否则保持原输出函数。"""
if output_func is not builtins.print: if output_func is not builtins.print:

View File

@ -1,4 +1,5 @@
import builtins import builtins
import sys
from pathlib import Path from pathlib import Path
import pytest import pytest
@ -370,3 +371,15 @@ def test_prompt_history_creates_runtime_dir(tmp_path: Path, monkeypatch):
assert callable(prompt) assert callable(prompt)
assert (tmp_path / "runtime").is_dir() assert (tmp_path / "runtime").is_dir()
def test_prompt_toolkit_enabled_when_frozen(tmp_path: Path, monkeypatch):
pytest.importorskip("prompt_toolkit")
monkeypatch.chdir(tmp_path)
monkeypatch.setattr(sys, "frozen", True, raising=False)
prompt = _build_prompt_input(builtins.input)
assert callable(prompt)
assert prompt is not builtins.input
assert (tmp_path / "runtime").is_dir()