pdd-cli 0.0.41__py3-none-any.whl → 0.0.43__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 +1 -1
- pdd/auto_deps_main.py +1 -1
- pdd/bug_main.py +1 -1
- pdd/change_main.py +1 -1
- pdd/cli.py +81 -3
- pdd/cmd_test_main.py +3 -3
- pdd/code_generator_main.py +3 -2
- pdd/conflicts_main.py +1 -1
- pdd/construct_paths.py +245 -19
- pdd/context_generator_main.py +27 -12
- pdd/crash_main.py +44 -51
- pdd/detect_change_main.py +1 -1
- pdd/fix_code_module_errors.py +12 -0
- pdd/fix_main.py +2 -2
- pdd/fix_verification_main.py +1 -1
- pdd/generate_output_paths.py +113 -21
- pdd/generate_test.py +53 -16
- pdd/llm_invoke.py +162 -0
- pdd/preprocess_main.py +1 -1
- pdd/prompts/sync_analysis_LLM.prompt +4 -4
- pdd/split_main.py +1 -1
- pdd/sync_determine_operation.py +921 -456
- pdd/sync_main.py +333 -0
- pdd/sync_orchestration.py +639 -0
- pdd/trace_main.py +1 -1
- pdd/update_main.py +7 -2
- pdd_cli-0.0.43.dist-info/METADATA +307 -0
- {pdd_cli-0.0.41.dist-info → pdd_cli-0.0.43.dist-info}/RECORD +32 -30
- pdd_cli-0.0.41.dist-info/METADATA +0 -269
- {pdd_cli-0.0.41.dist-info → pdd_cli-0.0.43.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.41.dist-info → pdd_cli-0.0.43.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.41.dist-info → pdd_cli-0.0.43.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.41.dist-info → pdd_cli-0.0.43.dist-info}/top_level.txt +0 -0
pdd/crash_main.py
CHANGED
|
@@ -52,15 +52,13 @@ def crash_main(
|
|
|
52
52
|
ctx.params = ctx.params if isinstance(ctx.params, dict) else {}
|
|
53
53
|
|
|
54
54
|
quiet = ctx.params.get("quiet", ctx.obj.get("quiet", False))
|
|
55
|
-
verbose = ctx.params.get("verbose", ctx.obj.get("verbose", False))
|
|
55
|
+
verbose = ctx.params.get("verbose", ctx.obj.get("verbose", False))
|
|
56
56
|
|
|
57
|
-
# Get model parameters from context early, including time
|
|
58
57
|
strength = ctx.obj.get("strength", DEFAULT_STRENGTH)
|
|
59
58
|
temperature = ctx.obj.get("temperature", 0)
|
|
60
|
-
time_param = ctx.obj.get("time", DEFAULT_TIME)
|
|
59
|
+
time_param = ctx.obj.get("time", DEFAULT_TIME)
|
|
61
60
|
|
|
62
61
|
try:
|
|
63
|
-
# Construct file paths
|
|
64
62
|
input_file_paths = {
|
|
65
63
|
"prompt_file": prompt_file,
|
|
66
64
|
"code_file": code_file,
|
|
@@ -73,9 +71,8 @@ def crash_main(
|
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
force = ctx.params.get("force", ctx.obj.get("force", False))
|
|
76
|
-
# quiet = ctx.params.get("quiet", ctx.obj.get("quiet", False)) # Already defined above
|
|
77
74
|
|
|
78
|
-
input_strings, output_file_paths,
|
|
75
|
+
resolved_config, input_strings, output_file_paths, language = construct_paths(
|
|
79
76
|
input_file_paths=input_file_paths,
|
|
80
77
|
force=force,
|
|
81
78
|
quiet=quiet,
|
|
@@ -83,78 +80,79 @@ def crash_main(
|
|
|
83
80
|
command_options=command_options
|
|
84
81
|
)
|
|
85
82
|
|
|
86
|
-
# Load input files
|
|
87
83
|
prompt_content = input_strings["prompt_file"]
|
|
88
84
|
code_content = input_strings["code_file"]
|
|
89
85
|
program_content = input_strings["program_file"]
|
|
90
86
|
error_content = input_strings["error_file"]
|
|
91
87
|
|
|
92
|
-
# Store original content for comparison later
|
|
93
88
|
original_code_content = code_content
|
|
94
89
|
original_program_content = program_content
|
|
95
90
|
|
|
96
|
-
# Get model parameters from context (strength, temperature, time already fetched)
|
|
97
|
-
# strength = ctx.obj.get("strength", DEFAULT_STRENGTH) # Moved up
|
|
98
|
-
# temperature = ctx.obj.get("temperature", 0) # Moved up
|
|
99
|
-
# time_budget = ctx.obj.get("time", DEFAULT_TIME) # Moved up and renamed
|
|
100
|
-
|
|
101
|
-
# verbose = ctx.params.get("verbose", ctx.obj.get("verbose", False)) # Already defined above
|
|
102
|
-
|
|
103
91
|
code_updated: bool = False
|
|
104
92
|
program_updated: bool = False
|
|
105
93
|
|
|
106
94
|
if loop:
|
|
107
|
-
# Use iterative fixing process
|
|
108
|
-
# Corrected parameter order for fix_code_loop, adding time_param
|
|
109
95
|
success, final_program, final_code, attempts, cost, model = fix_code_loop(
|
|
110
96
|
code_file, prompt_content, program_file, strength, temperature,
|
|
111
97
|
max_attempts or 3, budget or 5.0, error_file, verbose, time_param
|
|
112
98
|
)
|
|
113
|
-
#
|
|
114
|
-
if
|
|
115
|
-
|
|
116
|
-
|
|
99
|
+
# Always set final_code/final_program to something non-empty
|
|
100
|
+
if not final_code:
|
|
101
|
+
final_code = original_code_content
|
|
102
|
+
if not final_program:
|
|
103
|
+
final_program = original_program_content
|
|
104
|
+
code_updated = final_code != original_code_content
|
|
105
|
+
program_updated = final_program != original_program_content
|
|
117
106
|
else:
|
|
118
|
-
# Use single fix attempt
|
|
119
107
|
if fix_code_module_errors is None:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
program_content, prompt_content, code_content, error_content,
|
|
108
|
+
raise ImportError("fix_code_module_errors is required but not available.")
|
|
109
|
+
|
|
110
|
+
update_program, update_code, fixed_program, fixed_code, _, cost, model = fix_code_module_errors(
|
|
111
|
+
program_content, prompt_content, code_content, error_content,
|
|
112
|
+
strength, temperature, verbose, time_param
|
|
124
113
|
)
|
|
125
|
-
success = True
|
|
114
|
+
success = True
|
|
126
115
|
attempts = 1
|
|
127
|
-
# Use boolean flags from fix_code_module_errors and ensure content is not empty
|
|
128
|
-
code_updated = fm_update_code and bool(final_code)
|
|
129
|
-
program_updated = fm_update_program and bool(final_program)
|
|
130
116
|
|
|
131
|
-
|
|
132
|
-
|
|
117
|
+
# Fallback if fixed_program is empty but update_program is True
|
|
118
|
+
if update_program and not fixed_program.strip():
|
|
119
|
+
fixed_program = program_content
|
|
120
|
+
if update_code and not fixed_code.strip():
|
|
121
|
+
fixed_code = code_content
|
|
122
|
+
|
|
123
|
+
final_code = fixed_code if update_code else code_content
|
|
124
|
+
final_program = fixed_program if update_program else program_content
|
|
125
|
+
|
|
126
|
+
# Always set final_code/final_program to something non-empty
|
|
127
|
+
if not final_code:
|
|
128
|
+
final_code = original_code_content
|
|
129
|
+
if not final_program:
|
|
130
|
+
final_program = original_program_content
|
|
131
|
+
|
|
132
|
+
code_updated = final_code != original_code_content
|
|
133
|
+
program_updated = final_program != original_program_content
|
|
133
134
|
|
|
134
|
-
# Determine whether to write the files based on whether paths are provided
|
|
135
135
|
output_code_path_str = output_file_paths.get("output")
|
|
136
136
|
output_program_path_str = output_file_paths.get("output_program")
|
|
137
137
|
|
|
138
|
-
#
|
|
139
|
-
# Use fixed content if available, otherwise use original content
|
|
138
|
+
# Always write output files if output paths are specified
|
|
140
139
|
if output_code_path_str:
|
|
141
140
|
output_code_path = Path(output_code_path_str)
|
|
142
|
-
output_code_path.parent.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
output_code_path.parent.mkdir(parents=True, exist_ok=True)
|
|
143
142
|
with open(output_code_path, "w") as f:
|
|
144
|
-
f.write(final_code
|
|
143
|
+
f.write(final_code)
|
|
145
144
|
|
|
146
145
|
if output_program_path_str:
|
|
147
146
|
output_program_path = Path(output_program_path_str)
|
|
148
|
-
output_program_path.parent.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
output_program_path.parent.mkdir(parents=True, exist_ok=True)
|
|
149
148
|
with open(output_program_path, "w") as f:
|
|
150
|
-
f.write(final_program
|
|
149
|
+
f.write(final_program)
|
|
151
150
|
|
|
152
|
-
# Provide user feedback
|
|
153
151
|
if not quiet:
|
|
154
152
|
if success:
|
|
155
|
-
rprint("[bold green]Crash fix attempt completed.[/bold green]")
|
|
153
|
+
rprint("[bold green]Crash fix attempt completed.[/bold green]")
|
|
156
154
|
else:
|
|
157
|
-
rprint("[bold yellow]Crash fix attempt completed with issues
|
|
155
|
+
rprint("[bold yellow]Crash fix attempt completed with issues.[/bold yellow]")
|
|
158
156
|
rprint(f"[bold]Model used:[/bold] {model}")
|
|
159
157
|
rprint(f"[bold]Total attempts:[/bold] {attempts}")
|
|
160
158
|
rprint(f"[bold]Total cost:[/bold] ${cost:.2f}")
|
|
@@ -163,25 +161,20 @@ def crash_main(
|
|
|
163
161
|
if code_updated:
|
|
164
162
|
rprint(f"[bold]Fixed code saved to:[/bold] {output_code_path_str}")
|
|
165
163
|
else:
|
|
166
|
-
rprint(f"[info]Code file {Path(code_file).name} was not modified
|
|
167
|
-
|
|
164
|
+
rprint(f"[info]Code file '{Path(code_file).name}' was not modified (but output file was written).[/info]")
|
|
168
165
|
if output_program_path_str:
|
|
169
166
|
if program_updated:
|
|
170
167
|
rprint(f"[bold]Fixed program saved to:[/bold] {output_program_path_str}")
|
|
171
168
|
else:
|
|
172
|
-
rprint(f"[info]Program file {Path(program_file).name} was not modified
|
|
169
|
+
rprint(f"[info]Program file '{Path(program_file).name}' was not modified (but output file was written).[/info]")
|
|
173
170
|
|
|
174
171
|
return success, final_code, final_program, attempts, cost, model
|
|
175
|
-
|
|
172
|
+
|
|
176
173
|
except FileNotFoundError as e:
|
|
177
174
|
if not quiet:
|
|
178
|
-
|
|
179
|
-
rprint(f"[bold red]Error:[/bold red] Input file not found: {e}")
|
|
175
|
+
rprint(f"[bold red]Error:[/bold red] Input file not found: {e}")
|
|
180
176
|
sys.exit(1)
|
|
181
177
|
except Exception as e:
|
|
182
178
|
if not quiet:
|
|
183
179
|
rprint(f"[bold red]An unexpected error occurred:[/bold red] {str(e)}")
|
|
184
|
-
# Consider logging the full traceback here for debugging
|
|
185
|
-
# import traceback
|
|
186
|
-
# traceback.print_exc()
|
|
187
180
|
sys.exit(1)
|
pdd/detect_change_main.py
CHANGED
|
@@ -39,7 +39,7 @@ def detect_change_main(
|
|
|
39
39
|
"output": output
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
input_strings, output_file_paths, _ = construct_paths(
|
|
42
|
+
resolved_config, input_strings, output_file_paths, _ = construct_paths(
|
|
43
43
|
input_file_paths=input_file_paths,
|
|
44
44
|
force=ctx.obj.get('force', False),
|
|
45
45
|
quiet=ctx.obj.get('quiet', False),
|
pdd/fix_code_module_errors.py
CHANGED
|
@@ -81,6 +81,18 @@ def fix_code_module_errors(
|
|
|
81
81
|
model_name = first_response.get('model_name', '')
|
|
82
82
|
program_code_fix = first_response['result']
|
|
83
83
|
|
|
84
|
+
# Check if the LLM response is None or an error string
|
|
85
|
+
if program_code_fix is None:
|
|
86
|
+
error_msg = "LLM returned None result during error analysis"
|
|
87
|
+
if verbose:
|
|
88
|
+
print(f"[red]{error_msg}[/red]")
|
|
89
|
+
raise RuntimeError(error_msg)
|
|
90
|
+
elif isinstance(program_code_fix, str) and program_code_fix.startswith("ERROR:"):
|
|
91
|
+
error_msg = f"LLM failed to analyze errors: {program_code_fix}"
|
|
92
|
+
if verbose:
|
|
93
|
+
print(f"[red]{error_msg}[/red]")
|
|
94
|
+
raise RuntimeError(error_msg)
|
|
95
|
+
|
|
84
96
|
if verbose:
|
|
85
97
|
print("[green]Error analysis complete[/green]")
|
|
86
98
|
print(Markdown(program_code_fix))
|
pdd/fix_main.py
CHANGED
|
@@ -91,7 +91,7 @@ def fix_main(
|
|
|
91
91
|
"output_results": output_results
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
input_strings, output_file_paths, _ = construct_paths(
|
|
94
|
+
resolved_config, input_strings, output_file_paths, _ = construct_paths(
|
|
95
95
|
input_file_paths=input_file_paths,
|
|
96
96
|
force=ctx.obj.get('force', False),
|
|
97
97
|
quiet=ctx.obj.get('quiet', False),
|
|
@@ -177,7 +177,7 @@ def fix_main(
|
|
|
177
177
|
try:
|
|
178
178
|
# Get JWT token for cloud authentication
|
|
179
179
|
jwt_token = asyncio.run(get_jwt_token(
|
|
180
|
-
firebase_api_key=os.environ.get("
|
|
180
|
+
firebase_api_key=os.environ.get("NEXT_PUBLIC_FIREBASE_API_KEY"),
|
|
181
181
|
github_client_id=os.environ.get("GITHUB_CLIENT_ID"),
|
|
182
182
|
app_name="PDD Code Generator"
|
|
183
183
|
))
|
pdd/fix_verification_main.py
CHANGED
|
@@ -183,7 +183,7 @@ def fix_verification_main(
|
|
|
183
183
|
|
|
184
184
|
try:
|
|
185
185
|
# First try the official helper.
|
|
186
|
-
input_strings, output_file_paths, language = construct_paths(
|
|
186
|
+
resolved_config, input_strings, output_file_paths, language = construct_paths(
|
|
187
187
|
input_file_paths=input_file_paths,
|
|
188
188
|
force=force,
|
|
189
189
|
quiet=quiet,
|
pdd/generate_output_paths.py
CHANGED
|
@@ -26,6 +26,7 @@ COMMAND_OUTPUT_KEYS: Dict[str, List[str]] = {
|
|
|
26
26
|
'bug': ['output'],
|
|
27
27
|
'auto-deps': ['output'],
|
|
28
28
|
'verify': ['output_results', 'output_code', 'output_program'],
|
|
29
|
+
'sync': ['generate_output_path', 'test_output_path', 'example_output_path'],
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
# Define default filename patterns for each output key
|
|
@@ -48,8 +49,8 @@ DEFAULT_FILENAMES: Dict[str, Dict[str, str]] = {
|
|
|
48
49
|
},
|
|
49
50
|
'change': {'output': 'modified_{basename}.prompt'},
|
|
50
51
|
'update': {'output': 'modified_{basename}.prompt'}, # Consistent with change/split default
|
|
51
|
-
'detect': {'output': '{basename}_detect.csv'}, #
|
|
52
|
-
'conflicts': {'output': '{basename}_conflict.csv'}, #
|
|
52
|
+
'detect': {'output': '{basename}_detect.csv'}, # basename here is from change_file per construct_paths logic
|
|
53
|
+
'conflicts': {'output': '{basename}_conflict.csv'}, # basename here is combined sorted prompt basenames per construct_paths logic
|
|
53
54
|
'crash': {
|
|
54
55
|
'output': '{basename}_fixed{ext}',
|
|
55
56
|
# Using basename as program_basename isn't available here
|
|
@@ -63,6 +64,11 @@ DEFAULT_FILENAMES: Dict[str, Dict[str, str]] = {
|
|
|
63
64
|
'output_code': '{basename}_verified{ext}',
|
|
64
65
|
'output_program': '{basename}_program_verified{ext}',
|
|
65
66
|
},
|
|
67
|
+
'sync': {
|
|
68
|
+
'generate_output_path': '{basename}{ext}',
|
|
69
|
+
'test_output_path': 'test_{basename}{ext}',
|
|
70
|
+
'example_output_path': '{basename}_example{ext}',
|
|
71
|
+
},
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
# Define the mapping from command/output key to environment variables
|
|
@@ -96,6 +102,50 @@ ENV_VAR_MAP: Dict[str, Dict[str, str]] = {
|
|
|
96
102
|
'output_code': 'PDD_VERIFY_CODE_OUTPUT_PATH',
|
|
97
103
|
'output_program': 'PDD_VERIFY_PROGRAM_OUTPUT_PATH',
|
|
98
104
|
},
|
|
105
|
+
'sync': {
|
|
106
|
+
'generate_output_path': 'PDD_GENERATE_OUTPUT_PATH',
|
|
107
|
+
'test_output_path': 'PDD_TEST_OUTPUT_PATH',
|
|
108
|
+
'example_output_path': 'PDD_EXAMPLE_OUTPUT_PATH',
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Define mapping from context config keys to output keys for different commands
|
|
113
|
+
CONTEXT_CONFIG_MAP: Dict[str, Dict[str, str]] = {
|
|
114
|
+
'generate': {'output': 'generate_output_path'},
|
|
115
|
+
'example': {'output': 'example_output_path'},
|
|
116
|
+
'test': {'output': 'test_output_path'},
|
|
117
|
+
'sync': {
|
|
118
|
+
'generate_output_path': 'generate_output_path',
|
|
119
|
+
'test_output_path': 'test_output_path',
|
|
120
|
+
'example_output_path': 'example_output_path',
|
|
121
|
+
},
|
|
122
|
+
# For other commands, they can use the general mapping if needed
|
|
123
|
+
'preprocess': {'output': 'generate_output_path'}, # fallback
|
|
124
|
+
'fix': {
|
|
125
|
+
'output_test': 'test_output_path',
|
|
126
|
+
'output_code': 'generate_output_path',
|
|
127
|
+
'output_results': 'generate_output_path', # fallback for results
|
|
128
|
+
},
|
|
129
|
+
'split': {
|
|
130
|
+
'output_sub': 'generate_output_path', # fallback
|
|
131
|
+
'output_modified': 'generate_output_path', # fallback
|
|
132
|
+
},
|
|
133
|
+
'change': {'output': 'generate_output_path'},
|
|
134
|
+
'update': {'output': 'generate_output_path'},
|
|
135
|
+
'detect': {'output': 'generate_output_path'},
|
|
136
|
+
'conflicts': {'output': 'generate_output_path'},
|
|
137
|
+
'crash': {
|
|
138
|
+
'output': None, # Use default CWD behavior, not context paths
|
|
139
|
+
'output_program': None, # Use default CWD behavior, not context paths
|
|
140
|
+
},
|
|
141
|
+
'trace': {'output': 'generate_output_path'},
|
|
142
|
+
'bug': {'output': 'test_output_path'},
|
|
143
|
+
'auto-deps': {'output': 'generate_output_path'},
|
|
144
|
+
'verify': {
|
|
145
|
+
'output_results': 'generate_output_path',
|
|
146
|
+
'output_code': 'generate_output_path',
|
|
147
|
+
'output_program': 'generate_output_path',
|
|
148
|
+
},
|
|
99
149
|
}
|
|
100
150
|
|
|
101
151
|
# --- Helper Function ---
|
|
@@ -127,14 +177,15 @@ def generate_output_paths(
|
|
|
127
177
|
output_locations: Dict[str, Optional[str]],
|
|
128
178
|
basename: str,
|
|
129
179
|
language: str,
|
|
130
|
-
file_extension: str
|
|
180
|
+
file_extension: str,
|
|
181
|
+
context_config: Optional[Dict[str, str]] = None
|
|
131
182
|
) -> Dict[str, str]:
|
|
132
183
|
"""
|
|
133
184
|
Generates the full, absolute output paths for a given PDD command.
|
|
134
185
|
|
|
135
|
-
It prioritizes user-specified paths (--output options), then
|
|
136
|
-
|
|
137
|
-
current working directory.
|
|
186
|
+
It prioritizes user-specified paths (--output options), then context
|
|
187
|
+
configuration from .pddrc, then environment variables, and finally
|
|
188
|
+
falls back to default naming conventions in the current working directory.
|
|
138
189
|
|
|
139
190
|
Args:
|
|
140
191
|
command: The PDD command being executed (e.g., 'generate', 'fix').
|
|
@@ -146,6 +197,8 @@ def generate_output_paths(
|
|
|
146
197
|
language: The programming language associated with the operation.
|
|
147
198
|
file_extension: The file extension (including '.') for the language,
|
|
148
199
|
used when default patterns require it.
|
|
200
|
+
context_config: Optional dictionary with context-specific paths from .pddrc
|
|
201
|
+
configuration (e.g., {'generate_output_path': 'src/'}).
|
|
149
202
|
|
|
150
203
|
Returns:
|
|
151
204
|
A dictionary where keys are the standardized output identifiers
|
|
@@ -155,8 +208,10 @@ def generate_output_paths(
|
|
|
155
208
|
"""
|
|
156
209
|
logger.debug(f"Generating output paths for command: {command}")
|
|
157
210
|
logger.debug(f"User output locations: {output_locations}")
|
|
211
|
+
logger.debug(f"Context config: {context_config}")
|
|
158
212
|
logger.debug(f"Basename: {basename}, Language: {language}, Extension: {file_extension}")
|
|
159
213
|
|
|
214
|
+
context_config = context_config or {}
|
|
160
215
|
result_paths: Dict[str, str] = {}
|
|
161
216
|
|
|
162
217
|
if not basename:
|
|
@@ -183,6 +238,11 @@ def generate_output_paths(
|
|
|
183
238
|
logger.debug(f"Processing output key: {output_key}")
|
|
184
239
|
|
|
185
240
|
user_path: Optional[str] = processed_output_locations.get(output_key)
|
|
241
|
+
|
|
242
|
+
# Get context configuration path for this output key
|
|
243
|
+
context_config_key = CONTEXT_CONFIG_MAP.get(command, {}).get(output_key)
|
|
244
|
+
context_path: Optional[str] = context_config.get(context_config_key) if context_config_key else None
|
|
245
|
+
|
|
186
246
|
env_var_name: Optional[str] = ENV_VAR_MAP.get(command, {}).get(output_key)
|
|
187
247
|
env_path: Optional[str] = os.environ.get(env_var_name) if env_var_name else None
|
|
188
248
|
|
|
@@ -215,7 +275,26 @@ def generate_output_paths(
|
|
|
215
275
|
logger.debug(f"User path '{user_path}' identified as a specific file path.")
|
|
216
276
|
final_path = user_path # Assume it's a full path or filename
|
|
217
277
|
|
|
218
|
-
# 2. Check
|
|
278
|
+
# 2. Check Context Configuration Path (.pddrc)
|
|
279
|
+
elif context_path:
|
|
280
|
+
source = "context"
|
|
281
|
+
# Check if the context path is a directory
|
|
282
|
+
is_dir = context_path.endswith(os.path.sep) or context_path.endswith('/')
|
|
283
|
+
if not is_dir:
|
|
284
|
+
try:
|
|
285
|
+
if os.path.exists(context_path) and os.path.isdir(context_path):
|
|
286
|
+
is_dir = True
|
|
287
|
+
except Exception as e:
|
|
288
|
+
logger.warning(f"Could not check if context path '{context_path}' is a directory: {e}")
|
|
289
|
+
|
|
290
|
+
if is_dir:
|
|
291
|
+
logger.debug(f"Context path '{context_path}' identified as a directory.")
|
|
292
|
+
final_path = os.path.join(context_path, default_filename)
|
|
293
|
+
else:
|
|
294
|
+
logger.debug(f"Context path '{context_path}' identified as a specific file path.")
|
|
295
|
+
final_path = context_path
|
|
296
|
+
|
|
297
|
+
# 3. Check Environment Variable Path
|
|
219
298
|
elif env_path:
|
|
220
299
|
source = "environment"
|
|
221
300
|
# Check if the environment variable points to a directory
|
|
@@ -234,7 +313,7 @@ def generate_output_paths(
|
|
|
234
313
|
logger.debug(f"Env path '{env_path}' identified as a specific file path.")
|
|
235
314
|
final_path = env_path # Assume it's a full path or filename
|
|
236
315
|
|
|
237
|
-
#
|
|
316
|
+
# 4. Use Default Naming Convention in CWD
|
|
238
317
|
else:
|
|
239
318
|
source = "default"
|
|
240
319
|
logger.debug(f"Using default filename '{default_filename}' in current directory.")
|
|
@@ -273,7 +352,8 @@ if __name__ == '__main__':
|
|
|
273
352
|
output_locations={}, # No user input
|
|
274
353
|
basename=mock_basename,
|
|
275
354
|
language=mock_language,
|
|
276
|
-
file_extension=mock_extension
|
|
355
|
+
file_extension=mock_extension,
|
|
356
|
+
context_config={}
|
|
277
357
|
)
|
|
278
358
|
print(f"Result: {paths1}")
|
|
279
359
|
# Expected: {'output': '/path/to/cwd/my_module.py'}
|
|
@@ -285,7 +365,8 @@ if __name__ == '__main__':
|
|
|
285
365
|
output_locations={'output': 'generated_code.py'},
|
|
286
366
|
basename=mock_basename,
|
|
287
367
|
language=mock_language,
|
|
288
|
-
file_extension=mock_extension
|
|
368
|
+
file_extension=mock_extension,
|
|
369
|
+
context_config={}
|
|
289
370
|
)
|
|
290
371
|
print(f"Result: {paths2}")
|
|
291
372
|
# Expected: {'output': '/path/to/cwd/generated_code.py'}
|
|
@@ -300,7 +381,8 @@ if __name__ == '__main__':
|
|
|
300
381
|
output_locations={'output': test_dir_gen + os.path.sep}, # Explicit directory
|
|
301
382
|
basename=mock_basename,
|
|
302
383
|
language=mock_language,
|
|
303
|
-
file_extension=mock_extension
|
|
384
|
+
file_extension=mock_extension,
|
|
385
|
+
context_config={}
|
|
304
386
|
)
|
|
305
387
|
print(f"Result: {paths3}")
|
|
306
388
|
# Expected: {'output': '/path/to/cwd/temp_gen_output/my_module.py'}
|
|
@@ -319,7 +401,8 @@ if __name__ == '__main__':
|
|
|
319
401
|
},
|
|
320
402
|
basename=mock_basename,
|
|
321
403
|
language=mock_language,
|
|
322
|
-
file_extension=mock_extension
|
|
404
|
+
file_extension=mock_extension,
|
|
405
|
+
context_config={}
|
|
323
406
|
)
|
|
324
407
|
print(f"Result: {paths4}")
|
|
325
408
|
# Expected: {
|
|
@@ -344,7 +427,8 @@ if __name__ == '__main__':
|
|
|
344
427
|
output_locations={}, # No user input
|
|
345
428
|
basename=mock_basename,
|
|
346
429
|
language=mock_language,
|
|
347
|
-
file_extension=mock_extension
|
|
430
|
+
file_extension=mock_extension,
|
|
431
|
+
context_config={}
|
|
348
432
|
)
|
|
349
433
|
print(f"Result: {paths5}")
|
|
350
434
|
# Expected: {
|
|
@@ -365,7 +449,8 @@ if __name__ == '__main__':
|
|
|
365
449
|
output_locations={},
|
|
366
450
|
basename=mock_basename,
|
|
367
451
|
language=mock_language,
|
|
368
|
-
file_extension=mock_extension # This extension is ignored for preprocess default
|
|
452
|
+
file_extension=mock_extension, # This extension is ignored for preprocess default
|
|
453
|
+
context_config={}
|
|
369
454
|
)
|
|
370
455
|
print(f"Result: {paths6}")
|
|
371
456
|
# Expected: {'output': '/path/to/cwd/my_module_python_preprocessed.prompt'}
|
|
@@ -377,7 +462,8 @@ if __name__ == '__main__':
|
|
|
377
462
|
output_locations={},
|
|
378
463
|
basename=mock_basename,
|
|
379
464
|
language=mock_language,
|
|
380
|
-
file_extension=mock_extension
|
|
465
|
+
file_extension=mock_extension,
|
|
466
|
+
context_config={}
|
|
381
467
|
)
|
|
382
468
|
print(f"Result: {paths7}")
|
|
383
469
|
# Expected: {}
|
|
@@ -389,7 +475,8 @@ if __name__ == '__main__':
|
|
|
389
475
|
output_locations={},
|
|
390
476
|
basename="complex_prompt",
|
|
391
477
|
language="javascript",
|
|
392
|
-
file_extension=".js" # Ignored for split defaults
|
|
478
|
+
file_extension=".js", # Ignored for split defaults
|
|
479
|
+
context_config={}
|
|
393
480
|
)
|
|
394
481
|
print(f"Result: {paths8}")
|
|
395
482
|
# Expected: {
|
|
@@ -404,7 +491,8 @@ if __name__ == '__main__':
|
|
|
404
491
|
output_locations={},
|
|
405
492
|
basename="feature_analysis", # Used instead of change_file_basename
|
|
406
493
|
language="", # Not relevant for detect default
|
|
407
|
-
file_extension="" # Not relevant for detect default
|
|
494
|
+
file_extension="", # Not relevant for detect default
|
|
495
|
+
context_config={}
|
|
408
496
|
)
|
|
409
497
|
print(f"Result: {paths9}")
|
|
410
498
|
# Expected: {'output': '/path/to/cwd/feature_analysis_detect.csv'}
|
|
@@ -416,7 +504,8 @@ if __name__ == '__main__':
|
|
|
416
504
|
output_locations={},
|
|
417
505
|
basename="crashed_module", # Used for both code and program defaults
|
|
418
506
|
language="java",
|
|
419
|
-
file_extension=".java"
|
|
507
|
+
file_extension=".java",
|
|
508
|
+
context_config={}
|
|
420
509
|
)
|
|
421
510
|
print(f"Result: {paths10}")
|
|
422
511
|
# Expected: {
|
|
@@ -431,7 +520,8 @@ if __name__ == '__main__':
|
|
|
431
520
|
output_locations={},
|
|
432
521
|
basename="module_to_verify",
|
|
433
522
|
language="python",
|
|
434
|
-
file_extension=".py"
|
|
523
|
+
file_extension=".py",
|
|
524
|
+
context_config={}
|
|
435
525
|
)
|
|
436
526
|
print(f"Result: {paths11}")
|
|
437
527
|
# Expected: {
|
|
@@ -449,7 +539,8 @@ if __name__ == '__main__':
|
|
|
449
539
|
output_locations={'output_program': test_dir_verify_prog + os.path.sep},
|
|
450
540
|
basename="module_to_verify",
|
|
451
541
|
language="python",
|
|
452
|
-
file_extension=".py"
|
|
542
|
+
file_extension=".py",
|
|
543
|
+
context_config={}
|
|
453
544
|
)
|
|
454
545
|
print(f"Result: {paths12}")
|
|
455
546
|
# Expected: {
|
|
@@ -468,7 +559,8 @@ if __name__ == '__main__':
|
|
|
468
559
|
output_locations={},
|
|
469
560
|
basename="another_module_verify",
|
|
470
561
|
language="python",
|
|
471
|
-
file_extension=".py"
|
|
562
|
+
file_extension=".py",
|
|
563
|
+
context_config={}
|
|
472
564
|
)
|
|
473
565
|
print(f"Result: {paths13}")
|
|
474
566
|
# Expected: {
|
pdd/generate_test.py
CHANGED
|
@@ -72,19 +72,34 @@ def generate_test(
|
|
|
72
72
|
model_name = response['model_name']
|
|
73
73
|
result = response['result']
|
|
74
74
|
|
|
75
|
+
# Validate that we got a non-empty result
|
|
76
|
+
if not result or not result.strip():
|
|
77
|
+
raise ValueError(f"LLM test generation returned empty result. Model: {model_name}, Cost: ${response['cost']:.6f}")
|
|
78
|
+
|
|
75
79
|
if verbose:
|
|
76
80
|
console.print(Markdown(result))
|
|
77
81
|
console.print(f"[bold green]Initial generation cost: ${total_cost:.6f}[/bold green]")
|
|
78
82
|
|
|
79
83
|
# Step 4: Check if generation is complete
|
|
80
84
|
last_600_chars = result[-600:] if len(result) > 600 else result
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
|
|
86
|
+
# Validate that the last_600_chars is not empty after stripping
|
|
87
|
+
if not last_600_chars.strip():
|
|
88
|
+
# If the tail is empty, assume generation is complete
|
|
89
|
+
if verbose:
|
|
90
|
+
console.print("[bold yellow]Last 600 chars are empty, assuming generation is complete[/bold yellow]")
|
|
91
|
+
reasoning = "Generation appears complete (tail is empty)"
|
|
92
|
+
is_finished = True
|
|
93
|
+
check_cost = 0.0
|
|
94
|
+
check_model = model_name
|
|
95
|
+
else:
|
|
96
|
+
reasoning, is_finished, check_cost, check_model = unfinished_prompt(
|
|
97
|
+
prompt_text=last_600_chars,
|
|
98
|
+
strength=strength,
|
|
99
|
+
temperature=temperature,
|
|
100
|
+
time=time,
|
|
101
|
+
verbose=verbose
|
|
102
|
+
)
|
|
88
103
|
total_cost += check_cost
|
|
89
104
|
|
|
90
105
|
if not is_finished:
|
|
@@ -104,15 +119,37 @@ def generate_test(
|
|
|
104
119
|
model_name = continue_model
|
|
105
120
|
|
|
106
121
|
# Process the final result
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
try:
|
|
123
|
+
processed_result, post_cost, post_model = postprocess(
|
|
124
|
+
result,
|
|
125
|
+
language=language,
|
|
126
|
+
strength=EXTRACTION_STRENGTH,
|
|
127
|
+
temperature=temperature,
|
|
128
|
+
time=time,
|
|
129
|
+
verbose=verbose
|
|
130
|
+
)
|
|
131
|
+
total_cost += post_cost
|
|
132
|
+
except Exception as e:
|
|
133
|
+
console.print(f"[bold red]Postprocess failed: {str(e)}[/bold red]")
|
|
134
|
+
console.print(f"[bold yellow]Falling back to raw result[/bold yellow]")
|
|
135
|
+
|
|
136
|
+
# Try to extract code blocks directly from the raw result
|
|
137
|
+
import re
|
|
138
|
+
code_blocks = re.findall(r'```(?:python)?\s*(.*?)```', result, re.DOTALL | re.IGNORECASE)
|
|
139
|
+
|
|
140
|
+
if code_blocks:
|
|
141
|
+
# Use the first substantial code block
|
|
142
|
+
for block in code_blocks:
|
|
143
|
+
if len(block.strip()) > 100 and ('def test_' in block or 'import' in block):
|
|
144
|
+
processed_result = block.strip()
|
|
145
|
+
break
|
|
146
|
+
else:
|
|
147
|
+
processed_result = code_blocks[0].strip() if code_blocks else result
|
|
148
|
+
else:
|
|
149
|
+
# No code blocks found, use raw result
|
|
150
|
+
processed_result = result
|
|
151
|
+
|
|
152
|
+
post_cost = 0.0
|
|
116
153
|
|
|
117
154
|
# Step 5: Print total cost if verbose
|
|
118
155
|
if verbose:
|