Files
lyxy-document/scripts/core/advice_generator.py
lanyuanxiaoyao aaa1171e60 feat: 添加 --advice 参数,支持快速获取执行建议
- 新增 scripts/core/advice_generator.py 建议生成器模块
- 在 config.py 中添加 DEPENDENCIES 依赖配置
- 在 lyxy_document_reader.py 中添加 -a/--advice 参数
- 复用 Reader 实例的 supports 方法检测文件类型
- 支持平台检测,对 macOS x86_64 PDF 返回特殊命令
- 添加单元测试和集成测试
- 更新 SKILL.md,引导优先使用 --advice 参数
- 更新 README.md,添加项目结构说明
- 添加 openspec/specs/cli-advice/spec.md 规范文档
2026-03-09 18:13:00 +08:00

252 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""建议生成器模块,根据文件类型和平台返回执行建议。"""
import platform
from pathlib import Path
from typing import Dict, Optional, Tuple, List, Type
from config import DEPENDENCIES
from readers import BaseReader
from readers import (
PdfReader,
DocxReader,
XlsxReader,
PptxReader,
HtmlReader,
)
# Reader 类到配置 key 的映射
_READER_KEY_MAP: Dict[Type[BaseReader], str] = {
PdfReader: "pdf",
DocxReader: "docx",
XlsxReader: "xlsx",
PptxReader: "pptx",
HtmlReader: "html",
}
def detect_file_type_light(input_path: str, readers: List[BaseReader]) -> Optional[Type[BaseReader]]:
"""
轻量文件类型检测,复用 Reader 的 supports 方法。
Args:
input_path: 文件路径或 URL
readers: 已实例化的 reader 列表
Returns:
支持该输入的 Reader 类,无法识别返回 None
"""
for reader in readers:
if reader.supports(input_path):
return reader.__class__
return None
def get_platform() -> str:
"""
获取当前平台标识,格式为 {system}-{machine}
Returns:
平台标识,例如 "Darwin-arm64""Linux-x86_64""Windows-AMD64"
"""
system = platform.system()
machine = platform.machine()
return f"{system}-{machine}"
def get_dependencies(reader_cls: Type[BaseReader], platform_id: str) -> Tuple[Optional[str], list]:
"""
获取指定 Reader 类和平台的依赖配置。
Args:
reader_cls: Reader 类
platform_id: 平台标识(如 "Darwin-arm64"
Returns:
(python_version, dependencies) 元组
- python_version: 需要的 python 版本None 表示使用默认
- dependencies: 依赖包列表
"""
key = _READER_KEY_MAP.get(reader_cls)
if not key or key not in DEPENDENCIES:
return None, []
type_config = DEPENDENCIES[key]
# 先尝试匹配特定平台
if platform_id in type_config:
config = type_config[platform_id]
return config.get("python"), config.get("dependencies", [])
# 使用 default 配置
if "default" in type_config:
config = type_config["default"]
return config.get("python"), config.get("dependencies", [])
return None, []
def generate_uv_command(
dependencies: list,
input_path: str,
python_version: Optional[str] = None,
script_path: str = "scripts/lyxy_document_reader.py"
) -> str:
"""
生成 uv run 命令。
Args:
dependencies: 依赖包列表
input_path: 输入文件路径或 URL
python_version: 需要的 python 版本None 表示不指定
script_path: 脚本路径
Returns:
uv run 命令字符串
"""
parts = ["uv run"]
if python_version:
parts.append(f"--python {python_version}")
for dep in dependencies:
# 处理包含空格的依赖(如 unstructured[pdf]),需要加引号
if "[" in dep or " " in dep:
parts.append(f'--with "{dep}"')
else:
parts.append(f"--with {dep}")
parts.append(f"{script_path} {input_path}")
return " ".join(parts)
def generate_python_command(
dependencies: list,
input_path: str,
script_path: str = "scripts/lyxy_document_reader.py"
) -> Tuple[str, str]:
"""
生成 python 命令和 pip 安装命令。
Args:
dependencies: 依赖包列表
input_path: 输入文件路径或 URL
script_path: 脚本路径
Returns:
(python_command, pip_command) 元组
"""
python_cmd = f"python {script_path} {input_path}"
# 构建 pip install 命令,处理带引号的依赖
pip_parts = ["pip install"]
for dep in dependencies:
pip_parts.append(dep)
pip_cmd = " ".join(pip_parts)
return python_cmd, pip_cmd
def format_advice(
file_type: str,
input_path: str,
platform_id: str,
uv_command: str,
python_command: str,
pip_command: str,
has_platform_specific: bool = False
) -> str:
"""
格式化建议输出。
Args:
file_type: 文件类型
input_path: 输入路径
platform_id: 平台标识
uv_command: uv 命令
python_command: python 命令
pip_command: pip 安装命令
has_platform_specific: 是否使用了平台特殊配置
Returns:
格式化后的建议文本
"""
lines = []
# 文件类型和输入路径
lines.append(f"文件类型: {file_type.upper()}")
lines.append(f"输入路径: {input_path}")
# 平台信息(仅当使用了特殊配置时显示)
if has_platform_specific:
lines.append(f"平台: {platform_id}")
lines.append("")
# uv 命令
lines.append("[uv 命令]")
lines.append(uv_command)
lines.append("")
# python 命令
lines.append("[python 命令]")
lines.append(python_command)
lines.append(pip_command)
return "\n".join(lines)
def generate_advice(
input_path: str,
readers: List[BaseReader],
script_path: str = "scripts/lyxy_document_reader.py"
) -> Optional[str]:
"""
生成完整的执行建议。
Args:
input_path: 输入文件路径或 URL
readers: 已实例化的 reader 列表
script_path: 脚本路径
Returns:
格式化的建议文本,无法识别文件类型返回 None
"""
# 检测文件类型,获取 Reader 类
reader_cls = detect_file_type_light(input_path, readers)
if not reader_cls:
return None
# 获取配置 key 和显示名称
key = _READER_KEY_MAP.get(reader_cls, "unknown")
file_type = key
# 获取平台
platform_id = get_platform()
# 获取依赖配置
python_version, dependencies = get_dependencies(reader_cls, platform_id)
# 判断是否使用了平台特殊配置
has_platform_specific = False
if key in DEPENDENCIES:
type_config = DEPENDENCIES[key]
if platform_id in type_config and "default" in type_config:
has_platform_specific = True
# 生成命令
uv_command = generate_uv_command(dependencies, input_path, python_version, script_path)
python_command, pip_command = generate_python_command(dependencies, input_path, script_path)
# 格式化输出
return format_advice(
file_type,
input_path,
platform_id,
uv_command,
python_command,
pip_command,
has_platform_specific
)