feat(init): 添加初始化项目进度条以提升用户等待体验
This commit is contained in:
parent
eba9bd5863
commit
97de36207c
|
|
@ -1,16 +1,16 @@
|
||||||
# 需求工单:增强交互性 - 添加进度条显示
|
# 需求工单:增强交互性 - 添加进度条显示
|
||||||
name: 增强交互性:添加进度条显示
|
name: 增强交互性:添加进度条显示
|
||||||
description: |
|
description: |
|
||||||
当前工具在执行耗时操作(如初始化项目时生成多个文件、运行并行检查、自动修复循环)时,终端上仅打印日志信息,用户无法直观了解当前进度和剩余时间,导致等待体验不佳。
|
当前工具在执行耗时操作(如初始化项目时生成多个文件、运行并行检查、自动修复循环)时,用户无法直观了解当前进度,导致等待体验不佳。
|
||||||
希望利用 `rich` 库的进度条功能,在以下关键步骤中显示实时进度,提升用户体验:
|
希望利用 `rich` 库的进度条功能,在以下关键步骤中显示实时进度,提升用户体验:
|
||||||
|
|
||||||
1. **初始化项目(init)**:
|
1. **初始化项目(init)**:
|
||||||
- 在解析 `README.md` 生成 `design.json` 后,开始生成文件时,显示文件生成进度条。
|
- 在解析 `README.md` 生成 `design.json` 后,开始生成文件时,显示文件生成进度条。
|
||||||
- 进度条显示已生成文件数 / 总文件数,每个文件生成时显示当前文件名。
|
- 进度条显示已生成文件数 / 总文件数,每个文件生成时显示当前文件名。
|
||||||
- 若实现并发生成,进度条应动态更新已完成任务数,并可能显示每个文件的生成状态(如排队中、生成中、完成、失败)。
|
- 若实现并发生成,进度条应动态更新已完成任务数,并显示每个文件的生成状态(如排队中、生成中、完成、失败)。
|
||||||
|
|
||||||
2. **增强/修复模式(enhance/fix)**:
|
2. **增强/修复模式(enhance/fix)**:
|
||||||
- 在分析受影响文件、生成代码变更时,显示处理进度(例如分析中的文件数、生成补丁的进度)。
|
- 在分析受影响文件、生成代码变更时,显示处理进度(例如分析中的文件数、当前补丁的名称,剩余补丁的数量,完成补丁的数量等等)。
|
||||||
- 在运行检查工具时(`run_parallel_checks`),显示检查工具运行的进度条(已完成检查的文件数 / 总文件数)。
|
- 在运行检查工具时(`run_parallel_checks`),显示检查工具运行的进度条(已完成检查的文件数 / 总文件数)。
|
||||||
- 在自动修复循环中,显示每次修复尝试的进度(如第几次重试、剩余错误数)。
|
- 在自动修复循环中,显示每次修复尝试的进度(如第几次重试、剩余错误数)。
|
||||||
|
|
||||||
|
|
@ -22,9 +22,6 @@ description: |
|
||||||
|
|
||||||
需要修改的代码包括:
|
需要修改的代码包括:
|
||||||
- `cli.py`:主命令入口,控制整体流程,应在此处创建进度条上下文。
|
- `cli.py`:主命令入口,控制整体流程,应在此处创建进度条上下文。
|
||||||
- `core.py`:`CodeGenerator.generate_files` 方法(或类似方法)中,文件生成循环应集成进度条更新。
|
|
||||||
- `checker.py`:`run_parallel_checks`、`auto_fix` 和 `run_full_check_and_fix` 中,添加进度条。
|
|
||||||
- `utils.py`:可能添加辅助函数来统一创建进度条。
|
|
||||||
|
|
||||||
acceptance_criteria:
|
acceptance_criteria:
|
||||||
- 执行 `llm-codegen init` 时,终端显示一个清晰的文件生成进度条,实时更新已完成文件数。
|
- 执行 `llm-codegen init` 时,终端显示一个清晰的文件生成进度条,实时更新已完成文件数。
|
||||||
|
|
@ -35,7 +32,4 @@ acceptance_criteria:
|
||||||
- 进度条显示不会显著影响性能(更新频率合理)。
|
- 进度条显示不会显著影响性能(更新频率合理)。
|
||||||
|
|
||||||
affected_files:
|
affected_files:
|
||||||
- src/llm_codegen/cli.py
|
- src/llm_codegen/cli.py
|
||||||
- src/llm_codegen/core.py
|
|
||||||
- src/llm_codegen/checker.py
|
|
||||||
- src/llm_codegen/utils.py
|
|
||||||
|
|
@ -280,18 +280,23 @@ class Checker:
|
||||||
with ThreadPoolExecutor(max_workers=min(4, len(files))) as executor:
|
with ThreadPoolExecutor(max_workers=min(4, len(files))) as executor:
|
||||||
futures = [executor.submit(self.run_check, tool, file_path) for file_path in files]
|
futures = [executor.submit(self.run_check, tool, file_path) for file_path in files]
|
||||||
|
|
||||||
|
error_count = 0 # 初始化错误计数
|
||||||
for future in as_completed(futures):
|
for future in as_completed(futures):
|
||||||
try:
|
try:
|
||||||
result = future.result()
|
result = future.result()
|
||||||
all_results.append(result)
|
all_results.append(result)
|
||||||
|
# 检查并更新错误计数
|
||||||
|
if result.get("errors") and result["errors"]:
|
||||||
|
error_count += len(result["errors"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"并行检查任务失败: {e}")
|
logger.error(f"并行检查任务失败: {e}")
|
||||||
finally:
|
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)
|
self.save_results(all_results)
|
||||||
logger.info(f"并行检查完成,总结果数: {len(all_results)}")
|
logger.info(f"并行检查完成,总结果数: {len(all_results)},总错误数: {error_count}")
|
||||||
return all_results
|
return all_results
|
||||||
|
|
||||||
def save_results(self, results: List[Dict[str, Any]]) -> None:
|
def save_results(self, results: List[Dict[str, Any]]) -> None:
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ def init(
|
||||||
BarColumn(),
|
BarColumn(),
|
||||||
console=console,
|
console=console,
|
||||||
) as progress:
|
) as progress:
|
||||||
task_id = progress.add_task("正在初始化项目...", total=None)
|
task_id = progress.add_task("正在初始化项目...", total=1) # 修改:设置总任务数为1以控制进度显示
|
||||||
generator = CodeGenerator(
|
generator = CodeGenerator(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
base_url=base_url,
|
base_url=base_url,
|
||||||
|
|
@ -80,7 +80,7 @@ def init(
|
||||||
max_concurrency=max_concurrency,
|
max_concurrency=max_concurrency,
|
||||||
)
|
)
|
||||||
generator.run(readme)
|
generator.run(readme)
|
||||||
progress.update(task_id, description="初始化完成")
|
progress.update(task_id, completed=1, description="初始化完成") # 修改:更新完成状态
|
||||||
# 调用core.CodeGenerator.run并显示最终统计信息(假设从日志或生成器状态获取)
|
# 调用core.CodeGenerator.run并显示最终统计信息(假设从日志或生成器状态获取)
|
||||||
console.print("[green]生成完成。成功处理文件,详情请查看日志。[/green]")
|
console.print("[green]生成完成。成功处理文件,详情请查看日志。[/green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -134,40 +134,14 @@ def enhance(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"读取工单文件失败: {e}")
|
logger.error(f"读取工单文件失败: {e}")
|
||||||
raise typer.Exit(code=1)
|
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(
|
with Progress(
|
||||||
SpinnerColumn(),
|
SpinnerColumn(),
|
||||||
TextColumn("[progress.description]{task.description}"),
|
TextColumn("[progress.description]{task.description}"),
|
||||||
BarColumn(),
|
BarColumn(),
|
||||||
console=console,
|
console=console,
|
||||||
) as progress:
|
) as progress:
|
||||||
task_id = progress.add_task("正在增强项目...", total=None)
|
task_id = progress.add_task("正在增强项目...", total=1) # 修改:设置总任务数为1以控制进度显示
|
||||||
generator = CodeGenerator(
|
generator = CodeGenerator(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
base_url=base_url,
|
base_url=base_url,
|
||||||
|
|
@ -177,7 +151,10 @@ def enhance(
|
||||||
max_concurrency=max_concurrency,
|
max_concurrency=max_concurrency,
|
||||||
)
|
)
|
||||||
success = generator.process_issue(issue_content, issue_type="enhance")
|
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:
|
if not success:
|
||||||
logger.error("增强处理失败")
|
logger.error("增强处理失败")
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
|
|
@ -235,7 +212,7 @@ def fix(
|
||||||
BarColumn(),
|
BarColumn(),
|
||||||
console=console
|
console=console
|
||||||
) as progress:
|
) as progress:
|
||||||
task_id = progress.add_task("正在修复项目...", total=None)
|
task_id = progress.add_task("正在修复项目...", total=1) # 修改:设置总任务数为1以控制进度显示
|
||||||
generator = CodeGenerator(
|
generator = CodeGenerator(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
base_url=base_url,
|
base_url=base_url,
|
||||||
|
|
@ -245,7 +222,10 @@ def fix(
|
||||||
max_concurrency=max_concurrency,
|
max_concurrency=max_concurrency,
|
||||||
)
|
)
|
||||||
success = generator.process_issue(issue_content, issue_type="fix")
|
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:
|
if not success:
|
||||||
logger.error("修复处理失败")
|
logger.error("修复处理失败")
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
|
|
@ -253,7 +233,6 @@ def fix(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"修复失败: {e}")
|
logger.error(f"修复失败: {e}")
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
console.print("[green]修复处理完成。成功处理文件,详情请查看日志。[/green]")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
|
|
@ -305,7 +284,7 @@ def design(
|
||||||
BarColumn(),
|
BarColumn(),
|
||||||
console=console,
|
console=console,
|
||||||
) as progress:
|
) as progress:
|
||||||
task_id = progress.add_task("正在生成design.json...", total=None)
|
task_id = progress.add_task("正在生成design.json...", total=1) # 可选:保持现有风格,但工单未要求修改此命令
|
||||||
generator = CodeGenerator(
|
generator = CodeGenerator(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
base_url=base_url,
|
base_url=base_url,
|
||||||
|
|
@ -318,7 +297,7 @@ def design(
|
||||||
generator.readme_content = generator.parse_readme(file)
|
generator.readme_content = generator.parse_readme(file)
|
||||||
# 生成design.json
|
# 生成design.json
|
||||||
generator.generate_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]")
|
console.print(f"[green]✅ design.json 已生成在 {design_path}[/green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"生成design.json失败: {e}")
|
logger.error(f"生成design.json失败: {e}")
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from loguru import logger
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
|
||||||
from .utils import is_dangerous_command
|
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
|
from .diff_applier import parse_diff, apply_diff
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -692,8 +692,8 @@ class CodeGenerator:
|
||||||
file = queue.popleft()
|
file = queue.popleft()
|
||||||
future = executor.submit(self._generate_file_task, file, dependencies.get(file, []), processed_files)
|
future = executor.submit(self._generate_file_task, file, dependencies.get(file, []), processed_files)
|
||||||
futures[future] = file
|
futures[future] = file
|
||||||
# 为每个文件添加独立进度任务并保存任务ID
|
# 为每个文件添加独立进度任务并保存任务ID,添加状态显示
|
||||||
task_id = progress.add_task(f"生成 {file}", total=1)
|
task_id = progress.add_task(f"{file} - {FileStatus.GENERATING}", total=1) # 修改:添加状态
|
||||||
file_tasks[file] = task_id
|
file_tasks[file] = task_id
|
||||||
|
|
||||||
# 等待任意任务完成
|
# 等待任意任务完成
|
||||||
|
|
@ -702,14 +702,14 @@ class CodeGenerator:
|
||||||
file = futures.pop(future)
|
file = futures.pop(future)
|
||||||
try:
|
try:
|
||||||
success, error_msg = future.result()
|
success, error_msg = future.result()
|
||||||
# 更新文件进度任务
|
# 更新文件进度任务,根据状态更新描述
|
||||||
if file in file_tasks:
|
if file in file_tasks:
|
||||||
if success:
|
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]) # 移除任务
|
progress.remove_task(file_tasks[file]) # 移除任务
|
||||||
else:
|
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])
|
progress.remove_task(file_tasks[file])
|
||||||
del file_tasks[file] # 清理映射
|
del file_tasks[file] # 清理映射
|
||||||
if success:
|
if success:
|
||||||
|
|
@ -736,7 +736,7 @@ class CodeGenerator:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
# 然后执行和上面 `else` 分支相同的失败处理逻辑
|
# 然后执行和上面 `else` 分支相同的失败处理逻辑
|
||||||
if file in file_tasks:
|
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])
|
progress.remove_task(file_tasks[file])
|
||||||
del file_tasks[file] # 清理映射
|
del file_tasks[file] # 清理映射
|
||||||
logger.error(f"文件 {file} 生成失败,错误: {error_msg}")
|
logger.error(f"文件 {file} 生成失败,错误: {error_msg}")
|
||||||
|
|
@ -1208,4 +1208,4 @@ class CodeGenerator:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"同步README.md失败: {e}")
|
logger.error(f"同步README.md失败: {e}")
|
||||||
self.console.print(f"[bold red]❌ 同步README.md失败: {e}[/bold red]")
|
self.console.print(f"[bold red]❌ 同步README.md失败: {e}[/bold red]")
|
||||||
return False
|
return False
|
||||||
Loading…
Reference in New Issue