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.

Files changed (95) hide show
  1. pdd/__init__.py +0 -0
  2. pdd/auto_deps_main.py +98 -0
  3. pdd/auto_include.py +175 -0
  4. pdd/auto_update.py +73 -0
  5. pdd/bug_main.py +99 -0
  6. pdd/bug_to_unit_test.py +159 -0
  7. pdd/change.py +141 -0
  8. pdd/change_main.py +240 -0
  9. pdd/cli.py +607 -0
  10. pdd/cmd_test_main.py +155 -0
  11. pdd/code_generator.py +117 -0
  12. pdd/code_generator_main.py +66 -0
  13. pdd/comment_line.py +35 -0
  14. pdd/conflicts_in_prompts.py +143 -0
  15. pdd/conflicts_main.py +90 -0
  16. pdd/construct_paths.py +251 -0
  17. pdd/context_generator.py +133 -0
  18. pdd/context_generator_main.py +73 -0
  19. pdd/continue_generation.py +140 -0
  20. pdd/crash_main.py +127 -0
  21. pdd/data/language_format.csv +61 -0
  22. pdd/data/llm_model.csv +15 -0
  23. pdd/detect_change.py +142 -0
  24. pdd/detect_change_main.py +100 -0
  25. pdd/find_section.py +28 -0
  26. pdd/fix_code_loop.py +212 -0
  27. pdd/fix_code_module_errors.py +143 -0
  28. pdd/fix_error_loop.py +216 -0
  29. pdd/fix_errors_from_unit_tests.py +240 -0
  30. pdd/fix_main.py +138 -0
  31. pdd/generate_output_paths.py +194 -0
  32. pdd/generate_test.py +140 -0
  33. pdd/get_comment.py +55 -0
  34. pdd/get_extension.py +52 -0
  35. pdd/get_language.py +41 -0
  36. pdd/git_update.py +84 -0
  37. pdd/increase_tests.py +93 -0
  38. pdd/insert_includes.py +150 -0
  39. pdd/llm_invoke.py +304 -0
  40. pdd/load_prompt_template.py +59 -0
  41. pdd/pdd_completion.fish +72 -0
  42. pdd/pdd_completion.sh +141 -0
  43. pdd/pdd_completion.zsh +418 -0
  44. pdd/postprocess.py +121 -0
  45. pdd/postprocess_0.py +52 -0
  46. pdd/preprocess.py +199 -0
  47. pdd/preprocess_main.py +72 -0
  48. pdd/process_csv_change.py +182 -0
  49. pdd/prompts/auto_include_LLM.prompt +230 -0
  50. pdd/prompts/bug_to_unit_test_LLM.prompt +17 -0
  51. pdd/prompts/change_LLM.prompt +34 -0
  52. pdd/prompts/conflict_LLM.prompt +23 -0
  53. pdd/prompts/continue_generation_LLM.prompt +3 -0
  54. pdd/prompts/detect_change_LLM.prompt +65 -0
  55. pdd/prompts/example_generator_LLM.prompt +10 -0
  56. pdd/prompts/extract_auto_include_LLM.prompt +6 -0
  57. pdd/prompts/extract_code_LLM.prompt +22 -0
  58. pdd/prompts/extract_conflict_LLM.prompt +19 -0
  59. pdd/prompts/extract_detect_change_LLM.prompt +19 -0
  60. pdd/prompts/extract_program_code_fix_LLM.prompt +16 -0
  61. pdd/prompts/extract_prompt_change_LLM.prompt +7 -0
  62. pdd/prompts/extract_prompt_split_LLM.prompt +9 -0
  63. pdd/prompts/extract_prompt_update_LLM.prompt +8 -0
  64. pdd/prompts/extract_promptline_LLM.prompt +11 -0
  65. pdd/prompts/extract_unit_code_fix_LLM.prompt +332 -0
  66. pdd/prompts/extract_xml_LLM.prompt +7 -0
  67. pdd/prompts/fix_code_module_errors_LLM.prompt +17 -0
  68. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +62 -0
  69. pdd/prompts/generate_test_LLM.prompt +12 -0
  70. pdd/prompts/increase_tests_LLM.prompt +16 -0
  71. pdd/prompts/insert_includes_LLM.prompt +30 -0
  72. pdd/prompts/split_LLM.prompt +94 -0
  73. pdd/prompts/summarize_file_LLM.prompt +11 -0
  74. pdd/prompts/trace_LLM.prompt +30 -0
  75. pdd/prompts/trim_results_LLM.prompt +83 -0
  76. pdd/prompts/trim_results_start_LLM.prompt +45 -0
  77. pdd/prompts/unfinished_prompt_LLM.prompt +18 -0
  78. pdd/prompts/update_prompt_LLM.prompt +19 -0
  79. pdd/prompts/xml_convertor_LLM.prompt +54 -0
  80. pdd/split.py +119 -0
  81. pdd/split_main.py +103 -0
  82. pdd/summarize_directory.py +212 -0
  83. pdd/trace.py +135 -0
  84. pdd/trace_main.py +108 -0
  85. pdd/track_cost.py +102 -0
  86. pdd/unfinished_prompt.py +114 -0
  87. pdd/update_main.py +96 -0
  88. pdd/update_prompt.py +115 -0
  89. pdd/xml_tagger.py +122 -0
  90. pdd_cli-0.0.2.dist-info/LICENSE +7 -0
  91. pdd_cli-0.0.2.dist-info/METADATA +225 -0
  92. pdd_cli-0.0.2.dist-info/RECORD +95 -0
  93. pdd_cli-0.0.2.dist-info/WHEEL +5 -0
  94. pdd_cli-0.0.2.dist-info/entry_points.txt +2 -0
  95. 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