feat(init): 添加初始化项目进度条以提升用户等待体验

This commit is contained in:
songsenand 2026-03-19 00:53:33 +08:00
parent eba9bd5863
commit 97de36207c
4 changed files with 34 additions and 56 deletions

View File

@ -1,16 +1,16 @@
# 需求工单:增强交互性 - 添加进度条显示
name: 增强交互性:添加进度条显示
description: |
当前工具在执行耗时操作(如初始化项目时生成多个文件、运行并行检查、自动修复循环)时,终端上仅打印日志信息,用户无法直观了解当前进度和剩余时间,导致等待体验不佳。
当前工具在执行耗时操作(如初始化项目时生成多个文件、运行并行检查、自动修复循环)时,用户无法直观了解当前进度,导致等待体验不佳。
希望利用 `rich` 库的进度条功能,在以下关键步骤中显示实时进度,提升用户体验:
1. **初始化项目init**
- 在解析 `README.md` 生成 `design.json` 后,开始生成文件时,显示文件生成进度条。
- 进度条显示已生成文件数 / 总文件数,每个文件生成时显示当前文件名。
- 若实现并发生成,进度条应动态更新已完成任务数,并可能显示每个文件的生成状态(如排队中、生成中、完成、失败)。
- 若实现并发生成,进度条应动态更新已完成任务数,并显示每个文件的生成状态(如排队中、生成中、完成、失败)。
2. **增强/修复模式enhance/fix**
- 在分析受影响文件、生成代码变更时,显示处理进度(例如分析中的文件数、生成补丁的进度)。
- 在分析受影响文件、生成代码变更时,显示处理进度(例如分析中的文件数、当前补丁的名称,剩余补丁的数量,完成补丁的数量等等)。
- 在运行检查工具时(`run_parallel_checks`),显示检查工具运行的进度条(已完成检查的文件数 / 总文件数)。
- 在自动修复循环中,显示每次修复尝试的进度(如第几次重试、剩余错误数)。
@ -22,9 +22,6 @@ description: |
需要修改的代码包括:
- `cli.py`:主命令入口,控制整体流程,应在此处创建进度条上下文。
- `core.py``CodeGenerator.generate_files` 方法(或类似方法)中,文件生成循环应集成进度条更新。
- `checker.py``run_parallel_checks`、`auto_fix` 和 `run_full_check_and_fix` 中,添加进度条。
- `utils.py`:可能添加辅助函数来统一创建进度条。
acceptance_criteria:
- 执行 `llm-codegen init` 时,终端显示一个清晰的文件生成进度条,实时更新已完成文件数。
@ -36,6 +33,3 @@ acceptance_criteria:
affected_files:
- src/llm_codegen/cli.py
- src/llm_codegen/core.py
- src/llm_codegen/checker.py
- src/llm_codegen/utils.py

View File

@ -280,18 +280,23 @@ class Checker:
with ThreadPoolExecutor(max_workers=min(4, len(files))) as executor:
futures = [executor.submit(self.run_check, tool, file_path) for file_path in files]
error_count = 0 # 初始化错误计数
for future in as_completed(futures):
try:
result = future.result()
all_results.append(result)
# 检查并更新错误计数
if result.get("errors") and result["errors"]:
error_count += len(result["errors"])
except Exception as e:
logger.error(f"并行检查任务失败: {e}")
finally:
progress.update(task, advance=1)
# 更新进度条:前进并更新描述以显示错误统计
progress.update(task, advance=1, description=f"[cyan]Running parallel checks... Errors: {error_count}")
# 保存结果到文件
self.save_results(all_results)
logger.info(f"并行检查完成,总结果数: {len(all_results)}")
logger.info(f"并行检查完成,总结果数: {len(all_results)},总错误数: {error_count}")
return all_results
def save_results(self, results: List[Dict[str, Any]]) -> None:

View File

@ -70,7 +70,7 @@ def init(
BarColumn(),
console=console,
) as progress:
task_id = progress.add_task("正在初始化项目...", total=None)
task_id = progress.add_task("正在初始化项目...", total=1) # 修改设置总任务数为1以控制进度显示
generator = CodeGenerator(
api_key=api_key,
base_url=base_url,
@ -80,7 +80,7 @@ def init(
max_concurrency=max_concurrency,
)
generator.run(readme)
progress.update(task_id, description="初始化完成")
progress.update(task_id, completed=1, description="初始化完成") # 修改:更新完成状态
# 调用core.CodeGenerator.run并显示最终统计信息假设从日志或生成器状态获取
console.print("[green]生成完成。成功处理文件,详情请查看日志。[/green]")
except Exception as e:
@ -134,40 +134,14 @@ def enhance(
except Exception as e:
logger.error(f"读取工单文件失败: {e}")
raise typer.Exit(code=1)
"""
try:
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
BarColumn(),
console=console
) as progress:
task_id = progress.add_task("正在增强项目...", total=None)
generator = CodeGenerator(
api_key=api_key,
base_url=base_url,
model=model,
output_dir=str(output_dir),
log_file=log_file_path,
max_concurrency=max_concurrency,
)
success = generator.process_issue(issue_content, issue_type="enhance")
progress.update(task_id, description="增强处理完成")
if not success:
logger.error("增强处理失败")
raise typer.Exit(code=1)
console.print("[green]增强处理完成。成功处理文件,详情请查看日志。[/green]")
except Exception as e:
logger.error(f"增强失败: {e}")
raise typer.Exit(code=1)
"""
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
BarColumn(),
console=console,
) as progress:
task_id = progress.add_task("正在增强项目...", total=None)
task_id = progress.add_task("正在增强项目...", total=1) # 修改设置总任务数为1以控制进度显示
generator = CodeGenerator(
api_key=api_key,
base_url=base_url,
@ -177,7 +151,10 @@ def enhance(
max_concurrency=max_concurrency,
)
success = generator.process_issue(issue_content, issue_type="enhance")
progress.update(task_id, description="增强处理完成")
if success:
progress.update(task_id, completed=1, description="增强处理完成") # 修改:成功时更新完成状态
else:
progress.update(task_id, description="增强处理失败") # 修改:失败时更新描述
if not success:
logger.error("增强处理失败")
raise typer.Exit(code=1)
@ -235,7 +212,7 @@ def fix(
BarColumn(),
console=console
) as progress:
task_id = progress.add_task("正在修复项目...", total=None)
task_id = progress.add_task("正在修复项目...", total=1) # 修改设置总任务数为1以控制进度显示
generator = CodeGenerator(
api_key=api_key,
base_url=base_url,
@ -245,7 +222,10 @@ def fix(
max_concurrency=max_concurrency,
)
success = generator.process_issue(issue_content, issue_type="fix")
progress.update(task_id, description="修复处理完成")
if success:
progress.update(task_id, completed=1, description="修复处理完成") # 修改:成功时更新完成状态
else:
progress.update(task_id, description="修复处理失败") # 修改:失败时更新描述
if not success:
logger.error("修复处理失败")
raise typer.Exit(code=1)
@ -253,7 +233,6 @@ def fix(
except Exception as e:
logger.error(f"修复失败: {e}")
raise typer.Exit(code=1)
console.print("[green]修复处理完成。成功处理文件,详情请查看日志。[/green]")
@app.command()
@ -305,7 +284,7 @@ def design(
BarColumn(),
console=console,
) as progress:
task_id = progress.add_task("正在生成design.json...", total=None)
task_id = progress.add_task("正在生成design.json...", total=1) # 可选:保持现有风格,但工单未要求修改此命令
generator = CodeGenerator(
api_key=api_key,
base_url=base_url,
@ -318,7 +297,7 @@ def design(
generator.readme_content = generator.parse_readme(file)
# 生成design.json
generator.generate_design_json()
progress.update(task_id, description="design.json 生成完成")
progress.update(task_id, completed=1, description="design.json 生成完成") # 可选:更新完成状态
console.print(f"[green]✅ design.json 已生成在 {design_path}[/green]")
except Exception as e:
logger.error(f"生成design.json失败: {e}")

View File

@ -15,7 +15,7 @@ from loguru import logger
from openai import OpenAI
from .utils import is_dangerous_command
from .models import DesignModel, StateModel, FileModel
from .models import DesignModel, StateModel, FileModel, FileStatus # 添加 FileStatus 导入
from .diff_applier import parse_diff, apply_diff
@ -692,8 +692,8 @@ class CodeGenerator:
file = queue.popleft()
future = executor.submit(self._generate_file_task, file, dependencies.get(file, []), processed_files)
futures[future] = file
# 为每个文件添加独立进度任务并保存任务ID
task_id = progress.add_task(f"生成 {file}", total=1)
# 为每个文件添加独立进度任务并保存任务ID,添加状态显示
task_id = progress.add_task(f"{file} - {FileStatus.GENERATING}", total=1) # 修改:添加状态
file_tasks[file] = task_id
# 等待任意任务完成
@ -702,14 +702,14 @@ class CodeGenerator:
file = futures.pop(future)
try:
success, error_msg = future.result()
# 更新文件进度任务
# 更新文件进度任务,根据状态更新描述
if file in file_tasks:
if success:
progress.update(file_tasks[file], completed=1)
progress.update(file_tasks[file], completed=1, description=f"{file} - {FileStatus.SUCCESS}") # 修改:添加状态
progress.remove_task(file_tasks[file]) # 移除任务
else:
# 如果失败,标记为错误状态
progress.update(file_tasks[file], description=f"生成失败: {file}")
progress.update(file_tasks[file], description=f"{file} - {FileStatus.FAILED}: {error_msg}") # 修改:添加状态
progress.remove_task(file_tasks[file])
del file_tasks[file] # 清理映射
if success:
@ -736,7 +736,7 @@ class CodeGenerator:
error_msg = str(e)
# 然后执行和上面 `else` 分支相同的失败处理逻辑
if file in file_tasks:
progress.update(file_tasks[file], description=f"生成失败: {file}")
progress.update(file_tasks[file], description=f"{file} - {FileStatus.FAILED}: {error_msg}") # 修改:添加状态
progress.remove_task(file_tasks[file])
del file_tasks[file] # 清理映射
logger.error(f"文件 {file} 生成失败,错误: {error_msg}")