pdd-cli 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pdd-cli might be problematic. Click here for more details.
- pdd/__init__.py +0 -0
- pdd/auto_deps_main.py +98 -0
- pdd/auto_include.py +175 -0
- pdd/auto_update.py +73 -0
- pdd/bug_main.py +99 -0
- pdd/bug_to_unit_test.py +159 -0
- pdd/change.py +141 -0
- pdd/change_main.py +240 -0
- pdd/cli.py +607 -0
- pdd/cmd_test_main.py +155 -0
- pdd/code_generator.py +117 -0
- pdd/code_generator_main.py +66 -0
- pdd/comment_line.py +35 -0
- pdd/conflicts_in_prompts.py +143 -0
- pdd/conflicts_main.py +90 -0
- pdd/construct_paths.py +251 -0
- pdd/context_generator.py +133 -0
- pdd/context_generator_main.py +73 -0
- pdd/continue_generation.py +140 -0
- pdd/crash_main.py +127 -0
- pdd/data/language_format.csv +61 -0
- pdd/data/llm_model.csv +15 -0
- pdd/detect_change.py +142 -0
- pdd/detect_change_main.py +100 -0
- pdd/find_section.py +28 -0
- pdd/fix_code_loop.py +212 -0
- pdd/fix_code_module_errors.py +143 -0
- pdd/fix_error_loop.py +216 -0
- pdd/fix_errors_from_unit_tests.py +240 -0
- pdd/fix_main.py +138 -0
- pdd/generate_output_paths.py +194 -0
- pdd/generate_test.py +140 -0
- pdd/get_comment.py +55 -0
- pdd/get_extension.py +52 -0
- pdd/get_language.py +41 -0
- pdd/git_update.py +84 -0
- pdd/increase_tests.py +93 -0
- pdd/insert_includes.py +150 -0
- pdd/llm_invoke.py +304 -0
- pdd/load_prompt_template.py +59 -0
- pdd/pdd_completion.fish +72 -0
- pdd/pdd_completion.sh +141 -0
- pdd/pdd_completion.zsh +418 -0
- pdd/postprocess.py +121 -0
- pdd/postprocess_0.py +52 -0
- pdd/preprocess.py +199 -0
- pdd/preprocess_main.py +72 -0
- pdd/process_csv_change.py +182 -0
- pdd/prompts/auto_include_LLM.prompt +230 -0
- pdd/prompts/bug_to_unit_test_LLM.prompt +17 -0
- pdd/prompts/change_LLM.prompt +34 -0
- pdd/prompts/conflict_LLM.prompt +23 -0
- pdd/prompts/continue_generation_LLM.prompt +3 -0
- pdd/prompts/detect_change_LLM.prompt +65 -0
- pdd/prompts/example_generator_LLM.prompt +10 -0
- pdd/prompts/extract_auto_include_LLM.prompt +6 -0
- pdd/prompts/extract_code_LLM.prompt +22 -0
- pdd/prompts/extract_conflict_LLM.prompt +19 -0
- pdd/prompts/extract_detect_change_LLM.prompt +19 -0
- pdd/prompts/extract_program_code_fix_LLM.prompt +16 -0
- pdd/prompts/extract_prompt_change_LLM.prompt +7 -0
- pdd/prompts/extract_prompt_split_LLM.prompt +9 -0
- pdd/prompts/extract_prompt_update_LLM.prompt +8 -0
- pdd/prompts/extract_promptline_LLM.prompt +11 -0
- pdd/prompts/extract_unit_code_fix_LLM.prompt +332 -0
- pdd/prompts/extract_xml_LLM.prompt +7 -0
- pdd/prompts/fix_code_module_errors_LLM.prompt +17 -0
- pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +62 -0
- pdd/prompts/generate_test_LLM.prompt +12 -0
- pdd/prompts/increase_tests_LLM.prompt +16 -0
- pdd/prompts/insert_includes_LLM.prompt +30 -0
- pdd/prompts/split_LLM.prompt +94 -0
- pdd/prompts/summarize_file_LLM.prompt +11 -0
- pdd/prompts/trace_LLM.prompt +30 -0
- pdd/prompts/trim_results_LLM.prompt +83 -0
- pdd/prompts/trim_results_start_LLM.prompt +45 -0
- pdd/prompts/unfinished_prompt_LLM.prompt +18 -0
- pdd/prompts/update_prompt_LLM.prompt +19 -0
- pdd/prompts/xml_convertor_LLM.prompt +54 -0
- pdd/split.py +119 -0
- pdd/split_main.py +103 -0
- pdd/summarize_directory.py +212 -0
- pdd/trace.py +135 -0
- pdd/trace_main.py +108 -0
- pdd/track_cost.py +102 -0
- pdd/unfinished_prompt.py +114 -0
- pdd/update_main.py +96 -0
- pdd/update_prompt.py +115 -0
- pdd/xml_tagger.py +122 -0
- pdd_cli-0.0.2.dist-info/LICENSE +7 -0
- pdd_cli-0.0.2.dist-info/METADATA +225 -0
- pdd_cli-0.0.2.dist-info/RECORD +95 -0
- pdd_cli-0.0.2.dist-info/WHEEL +5 -0
- pdd_cli-0.0.2.dist-info/entry_points.txt +2 -0
- pdd_cli-0.0.2.dist-info/top_level.txt +1 -0
pdd/crash_main.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Tuple, Optional
|
|
3
|
+
import click
|
|
4
|
+
from rich import print as rprint
|
|
5
|
+
|
|
6
|
+
from .construct_paths import construct_paths
|
|
7
|
+
from .fix_code_loop import fix_code_loop
|
|
8
|
+
|
|
9
|
+
def crash_main(
|
|
10
|
+
ctx: click.Context,
|
|
11
|
+
prompt_file: str,
|
|
12
|
+
code_file: str,
|
|
13
|
+
program_file: str,
|
|
14
|
+
error_file: str,
|
|
15
|
+
output: Optional[str] = None,
|
|
16
|
+
output_program: Optional[str] = None,
|
|
17
|
+
loop: bool = False,
|
|
18
|
+
max_attempts: Optional[int] = None,
|
|
19
|
+
budget: Optional[float] = None
|
|
20
|
+
) -> Tuple[bool, str, str, int, float, str]:
|
|
21
|
+
"""
|
|
22
|
+
Main function to fix errors in a code module and its calling program that caused a crash.
|
|
23
|
+
|
|
24
|
+
:param ctx: Click context containing command-line parameters.
|
|
25
|
+
:param prompt_file: Path to the prompt file that generated the code module.
|
|
26
|
+
:param code_file: Path to the code module that caused the crash.
|
|
27
|
+
:param program_file: Path to the program that was running the code module.
|
|
28
|
+
:param error_file: Path to the file containing the error messages.
|
|
29
|
+
:param output: Optional path to save the fixed code file.
|
|
30
|
+
:param output_program: Optional path to save the fixed program file.
|
|
31
|
+
:param loop: Enable iterative fixing process.
|
|
32
|
+
:param max_attempts: Maximum number of fix attempts before giving up.
|
|
33
|
+
:param budget: Maximum cost allowed for the fixing process.
|
|
34
|
+
:return: A tuple containing:
|
|
35
|
+
- bool: Success status
|
|
36
|
+
- str: The final fixed code module
|
|
37
|
+
- str: The final fixed program
|
|
38
|
+
- int: Total number of fix attempts made
|
|
39
|
+
- float: Total cost of all fix attempts
|
|
40
|
+
- str: The name of the model used
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
# Construct file paths
|
|
44
|
+
input_file_paths = {
|
|
45
|
+
"prompt_file": prompt_file,
|
|
46
|
+
"code_file": code_file,
|
|
47
|
+
"program_file": program_file,
|
|
48
|
+
"error_file": error_file
|
|
49
|
+
}
|
|
50
|
+
command_options = {
|
|
51
|
+
"output": output,
|
|
52
|
+
"output_program": output_program
|
|
53
|
+
}
|
|
54
|
+
input_strings, output_file_paths, _ = construct_paths(
|
|
55
|
+
input_file_paths=input_file_paths,
|
|
56
|
+
force=ctx.obj.get('force', False),
|
|
57
|
+
quiet=ctx.obj.get('quiet', False),
|
|
58
|
+
command="crash",
|
|
59
|
+
command_options=command_options
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Load input files
|
|
63
|
+
prompt_content = input_strings["prompt_file"]
|
|
64
|
+
code_content = input_strings["code_file"]
|
|
65
|
+
program_content = input_strings["program_file"]
|
|
66
|
+
error_content = input_strings["error_file"]
|
|
67
|
+
|
|
68
|
+
# Get model parameters from context
|
|
69
|
+
strength = ctx.obj.get('strength', 0.9)
|
|
70
|
+
temperature = ctx.obj.get('temperature', 0)
|
|
71
|
+
|
|
72
|
+
if loop:
|
|
73
|
+
# Use iterative fixing process
|
|
74
|
+
success, final_code, final_program, attempts, cost, model = fix_code_loop(
|
|
75
|
+
code_file=code_file,
|
|
76
|
+
prompt=prompt_content,
|
|
77
|
+
verification_program=program_file,
|
|
78
|
+
strength=strength,
|
|
79
|
+
temperature=temperature,
|
|
80
|
+
max_attempts=max_attempts or 3,
|
|
81
|
+
budget=budget or 5.0,
|
|
82
|
+
error_log_file=error_file,
|
|
83
|
+
verbose=not ctx.obj.get('verbose', False)
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
# Use single fix attempt
|
|
87
|
+
from .fix_code_module_errors import fix_code_module_errors
|
|
88
|
+
_, _, final_program, final_code, cost, model = fix_code_module_errors(
|
|
89
|
+
program=program_content,
|
|
90
|
+
prompt=prompt_content,
|
|
91
|
+
code=code_content,
|
|
92
|
+
errors=error_content,
|
|
93
|
+
strength=strength,
|
|
94
|
+
temperature=temperature,
|
|
95
|
+
verbose=not ctx.obj.get('verbose', False)
|
|
96
|
+
)
|
|
97
|
+
success = True
|
|
98
|
+
attempts = 1
|
|
99
|
+
|
|
100
|
+
# Save results
|
|
101
|
+
if output_file_paths.get("output"):
|
|
102
|
+
with open(output_file_paths["output"], 'w') as f:
|
|
103
|
+
f.write(final_code)
|
|
104
|
+
if output_file_paths.get("output_program"):
|
|
105
|
+
with open(output_file_paths["output_program"], 'w') as f:
|
|
106
|
+
f.write(final_program)
|
|
107
|
+
|
|
108
|
+
# Provide user feedback
|
|
109
|
+
if not ctx.obj.get('quiet', False):
|
|
110
|
+
if success:
|
|
111
|
+
rprint("[bold green]Crash fix completed successfully.[/bold green]")
|
|
112
|
+
else:
|
|
113
|
+
rprint("[bold yellow]Crash fix completed with some issues.[/bold yellow]")
|
|
114
|
+
rprint(f"[bold]Model used:[/bold] {model}")
|
|
115
|
+
rprint(f"[bold]Total attempts:[/bold] {attempts}")
|
|
116
|
+
rprint(f"[bold]Total cost:[/bold] ${cost:.6f}")
|
|
117
|
+
if output:
|
|
118
|
+
rprint(f"[bold]Fixed code saved to:[/bold] {output_file_paths['output']}")
|
|
119
|
+
if output_program:
|
|
120
|
+
rprint(f"[bold]Fixed program saved to:[/bold] {output_file_paths['output_program']}")
|
|
121
|
+
|
|
122
|
+
return success, final_code, final_program, attempts, cost, model
|
|
123
|
+
|
|
124
|
+
except Exception as e:
|
|
125
|
+
if not ctx.obj.get('quiet', False):
|
|
126
|
+
rprint(f"[bold red]Error:[/bold red] {str(e)}")
|
|
127
|
+
sys.exit(1)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
language,comment,extension
|
|
2
|
+
Python,#,.py
|
|
3
|
+
Java,//,.java
|
|
4
|
+
C++,//,.cpp
|
|
5
|
+
JavaScript,//,.js
|
|
6
|
+
HTML,"<!-- -->",.html
|
|
7
|
+
CSS,"/**/",.css
|
|
8
|
+
Ruby,#,.rb
|
|
9
|
+
PHP,//,.php
|
|
10
|
+
Swift,//,.swift
|
|
11
|
+
Go,//,.go
|
|
12
|
+
Rust,//,.rs
|
|
13
|
+
Kotlin,//,.kt
|
|
14
|
+
TypeScript,//,.ts
|
|
15
|
+
C#,//,.cs
|
|
16
|
+
SQL,--,.sql
|
|
17
|
+
Shell,#,.sh
|
|
18
|
+
Bash,#,.sh
|
|
19
|
+
Fish,#,.fish
|
|
20
|
+
Zsh,#,.zsh
|
|
21
|
+
PowerShell,#,.ps1
|
|
22
|
+
Perl,#,.pl
|
|
23
|
+
R,#,.R
|
|
24
|
+
MATLAB,%,.m
|
|
25
|
+
Lua,--,.lua
|
|
26
|
+
Haskell,--,.hs
|
|
27
|
+
Scala,//,.scala
|
|
28
|
+
Groovy,//,.groovy
|
|
29
|
+
Dart,//,.dart
|
|
30
|
+
F#,//,.fs
|
|
31
|
+
YAML,#,.yml
|
|
32
|
+
JSON,del,.json
|
|
33
|
+
XML,"<!-- -->",.xml
|
|
34
|
+
Makefile,#,
|
|
35
|
+
CSV,del,.csv
|
|
36
|
+
Markdown,del,.md
|
|
37
|
+
LaTeX,%,.tex
|
|
38
|
+
Assembly,;,.asm
|
|
39
|
+
Fortran,!,.f90
|
|
40
|
+
COBOL,*>,.cob
|
|
41
|
+
Lisp,;,.lisp
|
|
42
|
+
Prolog,%,.pl
|
|
43
|
+
Erlang,%,.erl
|
|
44
|
+
Clojure,;,.clj
|
|
45
|
+
Julia,#,.jl
|
|
46
|
+
Elixir,#,.ex
|
|
47
|
+
Pascal,//,.pas
|
|
48
|
+
VBScript,"'",.vbs
|
|
49
|
+
CoffeeScript,#,.coffee
|
|
50
|
+
Objective-C,//,.m
|
|
51
|
+
Scheme,;,.scm
|
|
52
|
+
Tcl,#,.tcl
|
|
53
|
+
D,//,.d
|
|
54
|
+
Ada,--,.ada
|
|
55
|
+
Nim,#,.nim
|
|
56
|
+
OCaml,"(**)",.ml
|
|
57
|
+
LLM,del,.prompt
|
|
58
|
+
prompt,del,.prompt
|
|
59
|
+
TOML,#,.toml
|
|
60
|
+
Log,del,.log
|
|
61
|
+
reStructuredText,del,.rst
|
pdd/data/llm_model.csv
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
provider,model,input,output,coding_arena_elo,base_url,api_key,counter,encoder,max_tokens,max_completion_tokens,structured_output
|
|
2
|
+
OpenAI,"o1-mini-2024-09-12",3,12,1301,,,tiktoken,o200k_base,,65536,False
|
|
3
|
+
OpenAI,"deepseek-coder",0.14,0.28,1256,"https://api.deepseek.com/beta","DEEPSEEK_API_KEY",autotokenizer,deepseek-coder-7b-instruct-v1.5,8192,,False
|
|
4
|
+
Ollama,"qwen2.5-coder:32b-instruct-fp16",0.0,0.0,1227,,,,,,,False
|
|
5
|
+
Ollama,"athene-v2:72b-q8_0",0.0,0.0,1253,,,,,,,False
|
|
6
|
+
Anthropic,"claude-3-5-sonnet-20241022",3,15,1309,,,anthropic,claude-3-sonnet-20240229,8192,,False
|
|
7
|
+
Google,"gemini-2.0-flash-exp",0.15,0.60,1281,,,,,8192,,False
|
|
8
|
+
Fireworks,"accounts/fireworks/models/llama-v3p3-70b-instruct",3,3,1280,,,,,16384,,False
|
|
9
|
+
Fireworks,"accounts/fireworks/models/qwen2p5-coder-32b-instruct",.9,.9,1226,,,,,2048,,False
|
|
10
|
+
OpenAI,"gpt-4o-mini",0.15,0.60,1246,,,tiktoken,o200k_base,16384,,True
|
|
11
|
+
OpenAI,"gpt-4o-2024-11-20",2.5,10,1306,,,tiktoken,o200k_base,16384,,True
|
|
12
|
+
OpenAI,"o1-2024-12-17",15,60,1311,,,tiktoken,o200k_base,,32768,False
|
|
13
|
+
OpenAI,"grok-beta",5,15,1255,"https://api.x.ai/v1","XAI_API_KEY",tiktoken,o200k_base,4096,,False
|
|
14
|
+
Google,"gemini-exp-1206",0.15,0.60,1321,,,,,8192,,False
|
|
15
|
+
Anthropic,"claude-3-5-haiku-20241022",1,5,1265,,,anthropic,claude-3-sonnet-20240229,8192,,False
|
pdd/detect_change.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from typing import List, Dict, Tuple
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.markdown import Markdown
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from .preprocess import preprocess
|
|
8
|
+
from .load_prompt_template import load_prompt_template
|
|
9
|
+
from .llm_invoke import llm_invoke
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
class ChangeInstruction(BaseModel):
|
|
14
|
+
prompt_name: str = Field(description="Name of the prompt file that needs changes")
|
|
15
|
+
change_instructions: str = Field(description="Detailed instructions for the changes needed")
|
|
16
|
+
|
|
17
|
+
class ChangesList(BaseModel):
|
|
18
|
+
changes_list: List[ChangeInstruction] = Field(description="List of changes to be made")
|
|
19
|
+
|
|
20
|
+
def detect_change(
|
|
21
|
+
prompt_files: List[str],
|
|
22
|
+
change_description: str,
|
|
23
|
+
strength: float,
|
|
24
|
+
temperature: float,
|
|
25
|
+
verbose: bool = False
|
|
26
|
+
) -> Tuple[List[Dict[str, str]], float, str]:
|
|
27
|
+
"""
|
|
28
|
+
Analyze prompt files and determine which ones need changes based on a change description.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
prompt_files (List[str]): List of prompt file names to analyze
|
|
32
|
+
change_description (str): Description of the changes to analyze
|
|
33
|
+
strength (float): Strength parameter for the LLM model
|
|
34
|
+
temperature (float): Temperature parameter for the LLM model
|
|
35
|
+
verbose (bool): Whether to print detailed information
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Tuple[List[Dict[str, str]], float, str]: Changes list, total cost, and model name
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
# Step 1: Load and preprocess prompt templates
|
|
42
|
+
detect_change_prompt = load_prompt_template("detect_change_LLM")
|
|
43
|
+
if not detect_change_prompt:
|
|
44
|
+
raise ValueError("Failed to load detect_change_LLM prompt template")
|
|
45
|
+
|
|
46
|
+
extract_prompt = load_prompt_template("extract_detect_change_LLM")
|
|
47
|
+
if not extract_prompt:
|
|
48
|
+
raise ValueError("Failed to load extract_detect_change_LLM prompt template")
|
|
49
|
+
|
|
50
|
+
# Preprocess detect_change prompt
|
|
51
|
+
processed_detect_prompt = preprocess(
|
|
52
|
+
detect_change_prompt,
|
|
53
|
+
recursive=False,
|
|
54
|
+
double_curly_brackets=True,
|
|
55
|
+
exclude_keys=["PROMPT_LIST", "CHANGE_DESCRIPTION"]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Step 2: Create prompt list and process change description
|
|
59
|
+
prompt_list = []
|
|
60
|
+
total_cost = 0.0
|
|
61
|
+
|
|
62
|
+
for prompt_file in prompt_files:
|
|
63
|
+
try:
|
|
64
|
+
with open(prompt_file, 'r') as f:
|
|
65
|
+
prompt_content = f.read()
|
|
66
|
+
prompt_list.append({
|
|
67
|
+
"PROMPT_NAME": Path(prompt_file).name,
|
|
68
|
+
"PROMPT_DESCRIPTION": prompt_content
|
|
69
|
+
})
|
|
70
|
+
except FileNotFoundError:
|
|
71
|
+
console.print(f"[red]Warning: Could not find prompt file: {prompt_file}[/red]")
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
processed_change_description = preprocess(
|
|
75
|
+
change_description,
|
|
76
|
+
recursive=False,
|
|
77
|
+
double_curly_brackets=False
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Run initial LLM analysis
|
|
81
|
+
detect_response = llm_invoke(
|
|
82
|
+
prompt=processed_detect_prompt,
|
|
83
|
+
input_json={
|
|
84
|
+
"PROMPT_LIST": prompt_list,
|
|
85
|
+
"CHANGE_DESCRIPTION": processed_change_description
|
|
86
|
+
},
|
|
87
|
+
strength=strength,
|
|
88
|
+
temperature=temperature,
|
|
89
|
+
verbose=verbose
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if verbose:
|
|
93
|
+
console.print("[bold blue]Initial Analysis Results:[/bold blue]")
|
|
94
|
+
console.print(f"Token count: {detect_response.get('token_count', 0)}")
|
|
95
|
+
console.print(f"Cost: ${detect_response.get('cost', 0):.6f}")
|
|
96
|
+
|
|
97
|
+
total_cost += detect_response.get('cost', 0)
|
|
98
|
+
model_name = detect_response.get('model_name', '')
|
|
99
|
+
|
|
100
|
+
# Step 3: Extract specific changes
|
|
101
|
+
extract_response = llm_invoke(
|
|
102
|
+
prompt=extract_prompt,
|
|
103
|
+
input_json={"llm_output": detect_response['result']},
|
|
104
|
+
strength=0.89,
|
|
105
|
+
temperature=0.0,
|
|
106
|
+
verbose=verbose,
|
|
107
|
+
output_pydantic=ChangesList
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
total_cost += extract_response.get('cost', 0)
|
|
111
|
+
|
|
112
|
+
if verbose:
|
|
113
|
+
console.print("[bold blue]Extraction Results:[/bold blue]")
|
|
114
|
+
console.print(f"Token count: {extract_response.get('token_count', 0)}")
|
|
115
|
+
console.print(f"Cost: ${extract_response.get('cost', 0):.6f}")
|
|
116
|
+
|
|
117
|
+
# Step 4: Format and display results
|
|
118
|
+
changes_list = extract_response['result'].changes_list
|
|
119
|
+
if verbose:
|
|
120
|
+
console.print("\n[bold green]Detected Changes:[/bold green]")
|
|
121
|
+
for change in changes_list:
|
|
122
|
+
md = Markdown(f"""
|
|
123
|
+
### Prompt: {change.prompt_name}
|
|
124
|
+
**Change Instructions:**
|
|
125
|
+
{change.change_instructions}
|
|
126
|
+
""")
|
|
127
|
+
console.print(md)
|
|
128
|
+
console.print(f"\n[bold]Total Cost: ${total_cost:.6f}[/bold]")
|
|
129
|
+
console.print(f"[bold]Model Used: {model_name}[/bold]")
|
|
130
|
+
|
|
131
|
+
# Step 5: Return results
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
"prompt_name": change.prompt_name,
|
|
135
|
+
"change_instructions": change.change_instructions
|
|
136
|
+
}
|
|
137
|
+
for change in changes_list
|
|
138
|
+
], total_cost, model_name
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
console.print(f"[red]Error in detect_change: {str(e)}[/red]")
|
|
142
|
+
raise
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import sys
|
|
3
|
+
from typing import List, Dict, Tuple, Optional
|
|
4
|
+
import click
|
|
5
|
+
from rich import print as rprint
|
|
6
|
+
|
|
7
|
+
from .construct_paths import construct_paths
|
|
8
|
+
from .detect_change import detect_change
|
|
9
|
+
|
|
10
|
+
def detect_change_main(
|
|
11
|
+
ctx: click.Context,
|
|
12
|
+
prompt_files: List[str],
|
|
13
|
+
change_file: str,
|
|
14
|
+
output: Optional[str]
|
|
15
|
+
) -> Tuple[List[Dict], float, str]:
|
|
16
|
+
"""
|
|
17
|
+
CLI wrapper function to analyze which prompts need to be changed based on a change description.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
ctx: Click context containing command-line parameters
|
|
21
|
+
prompt_files: List of filenames of prompts that may need to be changed
|
|
22
|
+
change_file: Filename containing the change description
|
|
23
|
+
output: Optional path to save the CSV file containing analysis results
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple containing:
|
|
27
|
+
- List of dictionaries with analysis results
|
|
28
|
+
- Total cost of the operation
|
|
29
|
+
- Name of the model used
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
# Construct file paths
|
|
33
|
+
input_file_paths = {
|
|
34
|
+
"change_file": change_file,
|
|
35
|
+
**{f"prompt_file_{i}": pf for i, pf in enumerate(prompt_files)}
|
|
36
|
+
}
|
|
37
|
+
command_options = {
|
|
38
|
+
"output": output
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
input_strings, output_file_paths, _ = construct_paths(
|
|
42
|
+
input_file_paths=input_file_paths,
|
|
43
|
+
force=ctx.obj.get('force', False),
|
|
44
|
+
quiet=ctx.obj.get('quiet', False),
|
|
45
|
+
command="detect",
|
|
46
|
+
command_options=command_options
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Get change description content
|
|
50
|
+
change_description = input_strings["change_file"]
|
|
51
|
+
|
|
52
|
+
# Get prompt contents
|
|
53
|
+
prompt_contents = [input_strings[f"prompt_file_{i}"] for i in range(len(prompt_files))]
|
|
54
|
+
|
|
55
|
+
# Get model parameters from context
|
|
56
|
+
strength = ctx.obj.get('strength', 0.9)
|
|
57
|
+
temperature = ctx.obj.get('temperature', 0)
|
|
58
|
+
|
|
59
|
+
# Analyze which prompts need changes
|
|
60
|
+
changes_list, total_cost, model_name = detect_change(
|
|
61
|
+
prompt_files,
|
|
62
|
+
change_description,
|
|
63
|
+
strength,
|
|
64
|
+
temperature,
|
|
65
|
+
verbose=not ctx.obj.get('quiet', False)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Save results to CSV if output path is specified
|
|
69
|
+
if output_file_paths.get("output"):
|
|
70
|
+
with open(output_file_paths["output"], 'w', newline='') as csvfile:
|
|
71
|
+
fieldnames = ['prompt_name', 'change_instructions']
|
|
72
|
+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
73
|
+
writer.writeheader()
|
|
74
|
+
for change in changes_list:
|
|
75
|
+
writer.writerow(change)
|
|
76
|
+
|
|
77
|
+
# Provide user feedback
|
|
78
|
+
if not ctx.obj.get('quiet', False):
|
|
79
|
+
rprint("[bold green]Change detection completed successfully.[/bold green]")
|
|
80
|
+
rprint(f"[bold]Model used:[/bold] {model_name}")
|
|
81
|
+
rprint(f"[bold]Total cost:[/bold] ${total_cost:.6f}")
|
|
82
|
+
if output:
|
|
83
|
+
rprint(f"[bold]Results saved to:[/bold] {output_file_paths['output']}")
|
|
84
|
+
|
|
85
|
+
# Display detected changes
|
|
86
|
+
rprint("\n[bold]Changes needed:[/bold]")
|
|
87
|
+
if changes_list:
|
|
88
|
+
for change in changes_list:
|
|
89
|
+
rprint(f"[bold]Prompt:[/bold] {change['prompt_name']}")
|
|
90
|
+
rprint(f"[bold]Instructions:[/bold] {change['change_instructions']}")
|
|
91
|
+
rprint("---")
|
|
92
|
+
else:
|
|
93
|
+
rprint("No changes needed for any of the analyzed prompts.")
|
|
94
|
+
|
|
95
|
+
return changes_list, total_cost, model_name
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
if not ctx.obj.get('quiet', False):
|
|
99
|
+
rprint(f"[bold red]Error:[/bold red] {str(e)}")
|
|
100
|
+
sys.exit(1)
|
pdd/find_section.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
def find_section(lines, start_index=0, sub_section=False):
|
|
2
|
+
sections = []
|
|
3
|
+
stack = []
|
|
4
|
+
|
|
5
|
+
for i, line in enumerate(lines):
|
|
6
|
+
line = line.strip()
|
|
7
|
+
|
|
8
|
+
if line.startswith('```'):
|
|
9
|
+
if len(line) > 3:
|
|
10
|
+
# Opening of a new block
|
|
11
|
+
stack.append((i, line[3:].strip()))
|
|
12
|
+
elif stack:
|
|
13
|
+
# Closing of a block
|
|
14
|
+
start, lang = stack.pop()
|
|
15
|
+
if not stack: # This is a top-level block
|
|
16
|
+
if start >= start_index:
|
|
17
|
+
if sub_section:
|
|
18
|
+
return [(lang, start, i)]
|
|
19
|
+
else:
|
|
20
|
+
sections.append((lang, start, i))
|
|
21
|
+
|
|
22
|
+
# Handle unclosed blocks
|
|
23
|
+
while stack:
|
|
24
|
+
start, lang = stack.pop()
|
|
25
|
+
if not stack and start >= start_index: # This is a top-level block
|
|
26
|
+
sections.append((lang, start, len(lines) - 1))
|
|
27
|
+
|
|
28
|
+
return sections
|
pdd/fix_code_loop.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import shutil
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich import print as rprint
|
|
6
|
+
from pdd.fix_code_module_errors import fix_code_module_errors
|
|
7
|
+
|
|
8
|
+
def fix_code_loop(
|
|
9
|
+
code_file: str,
|
|
10
|
+
prompt: str,
|
|
11
|
+
verification_program: str,
|
|
12
|
+
strength: float,
|
|
13
|
+
temperature: float,
|
|
14
|
+
max_attempts: int,
|
|
15
|
+
budget: float,
|
|
16
|
+
error_log_file: str = "error_code.log",
|
|
17
|
+
verbose: bool = False,
|
|
18
|
+
) -> tuple[bool, str, str, int, float, str]:
|
|
19
|
+
"""
|
|
20
|
+
Attempts to fix errors in a code module through multiple iterations.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
code_file: Path to the code file being tested.
|
|
24
|
+
prompt: Prompt that generated the code under test.
|
|
25
|
+
verification_program: Path to a Python program that verifies if the code runs correctly.
|
|
26
|
+
strength: Strength of the LLM model to use.
|
|
27
|
+
temperature: Temperature parameter for the LLM model.
|
|
28
|
+
max_attempts: Maximum number of fix attempts before giving up.
|
|
29
|
+
budget: Maximum cost allowed for the fixing process.
|
|
30
|
+
error_log_file: Path to the error log file (default: "error_code.log").
|
|
31
|
+
verbose: Enable detailed logging of the fixing process (default: False).
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
success: Whether the errors were successfully fixed.
|
|
35
|
+
final_program: Contents of the final verification program file.
|
|
36
|
+
final_code: Contents of the final code file.
|
|
37
|
+
total_attempts: Number of fix attempts made.
|
|
38
|
+
total_cost: Total cost of all fix attempts.
|
|
39
|
+
model_name: Name of the LLM model used.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
console = Console()
|
|
43
|
+
|
|
44
|
+
# Step 1: Remove existing error log file
|
|
45
|
+
if os.path.exists(error_log_file):
|
|
46
|
+
os.remove(error_log_file)
|
|
47
|
+
|
|
48
|
+
# Step 2: Initialize variables
|
|
49
|
+
total_attempts = 0
|
|
50
|
+
total_cost = 0.0
|
|
51
|
+
model_name = ""
|
|
52
|
+
|
|
53
|
+
# Check if verification program exists
|
|
54
|
+
if not os.path.exists(verification_program):
|
|
55
|
+
error_message = f"Error: Verification program not found at {verification_program}"
|
|
56
|
+
rprint(f"[bold red]{error_message}[/bold red]")
|
|
57
|
+
with open(error_log_file, "a") as f:
|
|
58
|
+
f.write(error_message + "\n")
|
|
59
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
60
|
+
|
|
61
|
+
# Create backup copies of the original files
|
|
62
|
+
original_verification_program = verification_program + ".original"
|
|
63
|
+
original_code_file = code_file + ".original"
|
|
64
|
+
shutil.copy(verification_program, original_verification_program)
|
|
65
|
+
shutil.copy(code_file, original_code_file)
|
|
66
|
+
|
|
67
|
+
# Step 3: Main loop
|
|
68
|
+
success = False
|
|
69
|
+
while total_attempts < max_attempts:
|
|
70
|
+
rprint(f"\n[bold blue]Attempt: {total_attempts + 1}[/bold blue]")
|
|
71
|
+
with open(error_log_file, "a") as f:
|
|
72
|
+
f.write(f"\nAttempt: {total_attempts + 1}\n")
|
|
73
|
+
|
|
74
|
+
# Run the verification program
|
|
75
|
+
try:
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
["python", verification_program],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
check=False,
|
|
81
|
+
)
|
|
82
|
+
with open(error_log_file, "a") as f:
|
|
83
|
+
f.write(result.stdout)
|
|
84
|
+
f.write(result.stderr)
|
|
85
|
+
|
|
86
|
+
# Check for successful execution
|
|
87
|
+
if result.returncode == 0:
|
|
88
|
+
rprint("[bold green]Code ran successfully![/bold green]")
|
|
89
|
+
success = True
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
except FileNotFoundError:
|
|
93
|
+
error_message = f"Error: Verification program not found at {verification_program}"
|
|
94
|
+
rprint(f"[bold red]{error_message}[/bold red]")
|
|
95
|
+
with open(error_log_file, "a") as f:
|
|
96
|
+
f.write(error_message + "\n")
|
|
97
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
98
|
+
|
|
99
|
+
# If we get here, code failed
|
|
100
|
+
rprint("[bold red]Code execution failed.[/bold red]")
|
|
101
|
+
with open(error_log_file, "r") as f:
|
|
102
|
+
error_message = f.read()
|
|
103
|
+
|
|
104
|
+
# Escape square brackets for Rich printing
|
|
105
|
+
escaped_error_message = error_message.replace("[", "\\[").replace("]", "\\]")
|
|
106
|
+
rprint(f"[bold red]Errors found:\n[/bold red]{escaped_error_message}")
|
|
107
|
+
|
|
108
|
+
# Create iteration backups
|
|
109
|
+
verification_program_backup = (verification_program.rsplit(".", 1)[0]
|
|
110
|
+
+ f"_{total_attempts + 1}."
|
|
111
|
+
+ verification_program.rsplit(".", 1)[1])
|
|
112
|
+
code_file_backup = (code_file.rsplit(".", 1)[0]
|
|
113
|
+
+ f"_{total_attempts + 1}."
|
|
114
|
+
+ code_file.rsplit(".", 1)[1])
|
|
115
|
+
shutil.copy(verification_program, verification_program_backup)
|
|
116
|
+
shutil.copy(code_file, code_file_backup)
|
|
117
|
+
|
|
118
|
+
# Read current file contents
|
|
119
|
+
try:
|
|
120
|
+
with open(verification_program, "r") as f:
|
|
121
|
+
program_content = f.read()
|
|
122
|
+
with open(code_file, "r") as f:
|
|
123
|
+
code_content = f.read()
|
|
124
|
+
except FileNotFoundError as e:
|
|
125
|
+
rprint(f"[bold red]Error reading files: {e}[/bold red]")
|
|
126
|
+
with open(error_log_file, "a") as f:
|
|
127
|
+
f.write(f"Error reading files: {e}\n")
|
|
128
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
129
|
+
|
|
130
|
+
# Check budget before calling fix_code_module_errors
|
|
131
|
+
if total_cost >= budget:
|
|
132
|
+
rprint(f"[bold red]Budget exceeded. Stopping.[/bold red]")
|
|
133
|
+
success = False
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
# Call fix_code_module_errors
|
|
137
|
+
temp_console = Console(file=open(os.devnull, "w"), record=True)
|
|
138
|
+
with temp_console.capture() as capture:
|
|
139
|
+
update_program, update_code, fixed_program, fixed_code, cost, model_name = fix_code_module_errors(
|
|
140
|
+
program=program_content,
|
|
141
|
+
prompt=prompt,
|
|
142
|
+
code=code_content,
|
|
143
|
+
errors=error_message,
|
|
144
|
+
strength=strength,
|
|
145
|
+
temperature=temperature,
|
|
146
|
+
verbose=verbose,
|
|
147
|
+
)
|
|
148
|
+
captured_output = temp_console.export_text()
|
|
149
|
+
rprint(captured_output)
|
|
150
|
+
with open(error_log_file, "a") as f:
|
|
151
|
+
f.write(captured_output)
|
|
152
|
+
|
|
153
|
+
# Add the cost of this fix attempt
|
|
154
|
+
total_cost += cost
|
|
155
|
+
|
|
156
|
+
# Now increment attempts right after we’ve incurred cost
|
|
157
|
+
total_attempts += 1
|
|
158
|
+
|
|
159
|
+
# Check budget after fix
|
|
160
|
+
if total_cost > budget:
|
|
161
|
+
rprint("[bold red]Budget exceeded after fix attempt. Stopping.[/bold red]")
|
|
162
|
+
success = False
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
# If no changes to either file, nothing more to do
|
|
166
|
+
if not update_program and not update_code:
|
|
167
|
+
rprint("[bold yellow]No changes needed. Stopping.[/bold yellow]")
|
|
168
|
+
success = False
|
|
169
|
+
break
|
|
170
|
+
|
|
171
|
+
# Overwrite code file if updated
|
|
172
|
+
if update_code:
|
|
173
|
+
try:
|
|
174
|
+
with open(code_file, "w") as f:
|
|
175
|
+
f.write(fixed_code)
|
|
176
|
+
except FileNotFoundError as e:
|
|
177
|
+
rprint(f"[bold red]Error writing to code file: {e}[/bold red]")
|
|
178
|
+
with open(error_log_file, "a") as f:
|
|
179
|
+
f.write(f"Error writing to code file: {e}\n")
|
|
180
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
181
|
+
|
|
182
|
+
# Overwrite verification program if updated
|
|
183
|
+
if update_program:
|
|
184
|
+
try:
|
|
185
|
+
with open(verification_program, "w") as f:
|
|
186
|
+
f.write(fixed_program)
|
|
187
|
+
except FileNotFoundError as e:
|
|
188
|
+
rprint(f"[bold red]Error writing to verification program: {e}[/bold red]")
|
|
189
|
+
with open(error_log_file, "a") as f:
|
|
190
|
+
f.write(f"Error writing to verification program: {e}\n")
|
|
191
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
192
|
+
|
|
193
|
+
# Step 4: If not successful, restore the original files
|
|
194
|
+
if not success:
|
|
195
|
+
rprint("[bold yellow]Restoring original files.[/bold yellow]")
|
|
196
|
+
shutil.copy(original_verification_program, verification_program)
|
|
197
|
+
shutil.copy(original_code_file, code_file)
|
|
198
|
+
final_program = ""
|
|
199
|
+
final_code = ""
|
|
200
|
+
else:
|
|
201
|
+
try:
|
|
202
|
+
with open(verification_program, "r") as f:
|
|
203
|
+
final_program = f.read()
|
|
204
|
+
with open(code_file, "r") as f:
|
|
205
|
+
final_code = f.read()
|
|
206
|
+
except FileNotFoundError as e:
|
|
207
|
+
rprint(f"[bold red]Error reading final files: {e}[/bold red]")
|
|
208
|
+
with open(error_log_file, "a") as f:
|
|
209
|
+
f.write(f"Error reading final files: {e}\n")
|
|
210
|
+
return False, "", "", total_attempts, total_cost, model_name
|
|
211
|
+
|
|
212
|
+
return success, final_program, final_code, total_attempts, total_cost, model_name
|