fec4c09ee0
CI / test (push) Successful in 8s
doc_parser_skill: - New: verify_flowchart.py (flowchart validation) - Updated: LLM.py (multi-provider: DeepSeek + DashScope) - Updated: image_parser.py (logic tree support, external prompts) - Updated: SKILL.md, prompts/image_prompt.md conflict_detection_skill: - Updated: LLM.py (multi-provider sync) - Updated: detect_conflicts.py (logic tree text conversion) ir_generation_skill: - Replaced old scripts/LLM.py + ir_generator.py with standalone project - New: main.py, config.py, step1-3_*.py, ensemble_merge.py - New: prompts/, tests/ subdirectories tests: - New: acceptance/ test suite with schema validation - Fixed: conftest no longer globally skips non-acceptance tests - Updated: test_sample.py for new ir_generation structure Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
158 lines
5.4 KiB
Python
158 lines
5.4 KiB
Python
"""
|
|
IR Generation Pipeline Orchestrator.
|
|
|
|
Run all four stages sequentially:
|
|
python main.py [--skip-step1] [--skip-step2] [--skip-step2.5] [--skip-step3] [--test-only]
|
|
|
|
The pipeline reads the parsed PRD JSON from doc_parser and produces:
|
|
- ir_final.json: the final IR rules
|
|
- ir_audit_report.md: completeness audit report for human review
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import config
|
|
|
|
BASE_DIR = Path(__file__).parent
|
|
|
|
|
|
def _subprocess_env(extra: dict | None = None) -> dict:
|
|
"""Build environment dict for subprocesses, carrying forward overrides."""
|
|
env = os.environ.copy()
|
|
env.update(extra or {})
|
|
return env
|
|
|
|
|
|
def run_step(script_name: str, description: str, extra_env: dict | None = None) -> bool:
|
|
"""Run a single pipeline step script, return True if it succeeded."""
|
|
print(f"\n{'#' * 60}")
|
|
print(f"# {description}")
|
|
print(f"{'#' * 60}")
|
|
script_path = BASE_DIR / script_name
|
|
if not script_path.exists():
|
|
print(f"错误: 脚本不存在 {script_path}")
|
|
return False
|
|
result = subprocess.run(
|
|
[sys.executable, str(script_path)],
|
|
cwd=str(BASE_DIR),
|
|
env=_subprocess_env(extra_env),
|
|
)
|
|
return result.returncode == 0
|
|
|
|
|
|
def run_test(test_name: str, description: str, extra_env: dict | None = None) -> bool:
|
|
"""Run a test script, return True if all tests passed."""
|
|
print(f"\n{'='*60}")
|
|
print(f"测试: {description}")
|
|
print(f"{'='*60}")
|
|
test_path = BASE_DIR / "tests" / test_name
|
|
if not test_path.exists():
|
|
print(f"错误: 测试脚本不存在 {test_path}")
|
|
return False
|
|
result = subprocess.run(
|
|
[sys.executable, str(test_path)],
|
|
cwd=str(BASE_DIR),
|
|
env=_subprocess_env(extra_env),
|
|
)
|
|
return result.returncode == 0
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="IR Generation Pipeline")
|
|
parser.add_argument("--skip-step1", action="store_true",
|
|
help="跳过阶段一(语义索引)")
|
|
parser.add_argument("--skip-step2", action="store_true",
|
|
help="跳过阶段二(IR 提取)")
|
|
parser.add_argument("--skip-step2.5", "--skip-step2-5", action="store_true",
|
|
dest="skip_step2_5",
|
|
help="跳过阶段2.5(分支覆盖自动补全)")
|
|
parser.add_argument("--skip-step3", action="store_true",
|
|
help="跳过阶段三(合并与审计)")
|
|
parser.add_argument("--test-only", action="store_true",
|
|
help="仅运行测试,不调用 LLM")
|
|
parser.add_argument(
|
|
"--input", "-i", type=str, default=None,
|
|
help="输入 JSON 文件路径(覆盖默认的 doc_parser 输出)"
|
|
)
|
|
parser.add_argument(
|
|
"--provider", "-p", type=str, default=None,
|
|
help="LLM provider: deepseek | dashscope(覆盖 IR_PROVIDER 环境变量)"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
# Build extra env vars for subprocesses
|
|
extra_env = {}
|
|
if args.input:
|
|
extra_env["IR_INPUT_JSON"] = args.input
|
|
print(f"输入文件: {args.input}")
|
|
if args.provider:
|
|
extra_env["IR_PROVIDER"] = args.provider
|
|
print(f"LLM Provider: {args.provider}")
|
|
|
|
if args.test_only:
|
|
all_ok = True
|
|
all_ok &= run_test("test_step1.py", "Step 1 验证", extra_env)
|
|
all_ok &= run_test("test_step2.py", "Step 2 验证", extra_env)
|
|
all_ok &= run_test("test_step2_5.py", "Step 2.5 验证", extra_env)
|
|
all_ok &= run_test("test_step3.py", "Step 3 验证", extra_env)
|
|
sys.exit(0 if all_ok else 1)
|
|
|
|
failures = []
|
|
|
|
# Stage 1
|
|
if not args.skip_step1:
|
|
ok = run_step("step1_semantic_index.py",
|
|
"阶段一:宏观语义索引", extra_env)
|
|
if not ok:
|
|
failures.append("阶段一")
|
|
print("\n阶段一失败,停止流水线。修复后重试。")
|
|
sys.exit(1)
|
|
run_test("test_step1.py", "Step 1 验证", extra_env)
|
|
|
|
# Stage 2
|
|
if not args.skip_step2:
|
|
ok = run_step("step2_ir_extraction.py",
|
|
"阶段二:逐功能单元 IR 提取", extra_env)
|
|
if not ok:
|
|
failures.append("阶段二")
|
|
print("\n阶段二失败,停止流水线。修复后重试。")
|
|
sys.exit(1)
|
|
run_test("test_step2.py", "Step 2 验证", extra_env)
|
|
|
|
# Stage 2.5
|
|
if not args.skip_step2_5:
|
|
ok = run_step("step2_5_branch_coverage.py",
|
|
"阶段2.5:分支覆盖自动补全", extra_env)
|
|
if not ok:
|
|
failures.append("阶段2.5")
|
|
print("\n阶段2.5失败,停止流水线。修复后重试。")
|
|
sys.exit(1)
|
|
run_test("test_step2_5.py", "Step 2.5 验证", extra_env)
|
|
|
|
# Stage 3
|
|
if not args.skip_step3:
|
|
ok = run_step("step3_merge_and_audit.py",
|
|
"阶段三:确定性合并与完整性校验", extra_env)
|
|
if not ok:
|
|
failures.append("阶段三")
|
|
sys.exit(1)
|
|
run_test("test_step3.py", "Step 3 验证", extra_env)
|
|
|
|
if failures:
|
|
print(f"\n失败阶段: {', '.join(failures)}")
|
|
sys.exit(1)
|
|
|
|
print(f"\n{'='*60}")
|
|
print("流水线全部完成!")
|
|
print(f"最终 IR: {config.IR_FINAL_JSON}")
|
|
print(f"审计报告: {config.IR_AUDIT_REPORT_MD}")
|
|
print(f"{'='*60}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|