pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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.
- pdd/__init__.py +38 -6
- pdd/agentic_bug.py +323 -0
- pdd/agentic_bug_orchestrator.py +506 -0
- pdd/agentic_change.py +231 -0
- pdd/agentic_change_orchestrator.py +537 -0
- pdd/agentic_common.py +533 -770
- pdd/agentic_crash.py +2 -1
- pdd/agentic_e2e_fix.py +319 -0
- pdd/agentic_e2e_fix_orchestrator.py +582 -0
- pdd/agentic_fix.py +118 -3
- pdd/agentic_update.py +27 -9
- pdd/agentic_verify.py +3 -2
- pdd/architecture_sync.py +565 -0
- pdd/auth_service.py +210 -0
- pdd/auto_deps_main.py +63 -53
- pdd/auto_include.py +236 -3
- pdd/auto_update.py +125 -47
- pdd/bug_main.py +195 -23
- pdd/cmd_test_main.py +345 -197
- pdd/code_generator.py +4 -2
- pdd/code_generator_main.py +118 -32
- pdd/commands/__init__.py +6 -0
- pdd/commands/analysis.py +113 -48
- pdd/commands/auth.py +309 -0
- pdd/commands/connect.py +358 -0
- pdd/commands/fix.py +155 -114
- pdd/commands/generate.py +5 -0
- pdd/commands/maintenance.py +3 -2
- pdd/commands/misc.py +8 -0
- pdd/commands/modify.py +225 -163
- pdd/commands/sessions.py +284 -0
- pdd/commands/utility.py +12 -7
- pdd/construct_paths.py +334 -32
- pdd/context_generator_main.py +167 -170
- pdd/continue_generation.py +6 -3
- pdd/core/__init__.py +33 -0
- pdd/core/cli.py +44 -7
- pdd/core/cloud.py +237 -0
- pdd/core/dump.py +68 -20
- pdd/core/errors.py +4 -0
- pdd/core/remote_session.py +61 -0
- pdd/crash_main.py +219 -23
- pdd/data/llm_model.csv +4 -4
- pdd/docs/prompting_guide.md +864 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
- pdd/fix_code_loop.py +208 -34
- pdd/fix_code_module_errors.py +6 -2
- pdd/fix_error_loop.py +291 -38
- pdd/fix_main.py +208 -6
- pdd/fix_verification_errors_loop.py +235 -26
- pdd/fix_verification_main.py +269 -83
- pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
- pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
- pdd/frontend/dist/index.html +376 -0
- pdd/frontend/dist/logo.svg +33 -0
- pdd/generate_output_paths.py +46 -5
- pdd/generate_test.py +212 -151
- pdd/get_comment.py +19 -44
- pdd/get_extension.py +8 -9
- pdd/get_jwt_token.py +309 -20
- pdd/get_language.py +8 -7
- pdd/get_run_command.py +7 -5
- pdd/insert_includes.py +2 -1
- pdd/llm_invoke.py +531 -97
- pdd/load_prompt_template.py +15 -34
- pdd/operation_log.py +342 -0
- pdd/path_resolution.py +140 -0
- pdd/postprocess.py +122 -97
- pdd/preprocess.py +68 -12
- pdd/preprocess_main.py +33 -1
- pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
- pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
- pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
- pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
- pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
- pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
- pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
- pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
- pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
- pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
- pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
- pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
- pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
- pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
- pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
- pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
- pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
- pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
- pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
- pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
- pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
- pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
- pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
- pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
- pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
- pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
- pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
- pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
- pdd/prompts/agentic_update_LLM.prompt +192 -338
- pdd/prompts/auto_include_LLM.prompt +22 -0
- pdd/prompts/change_LLM.prompt +3093 -1
- pdd/prompts/detect_change_LLM.prompt +571 -14
- pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
- pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
- pdd/prompts/generate_test_LLM.prompt +19 -1
- pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
- pdd/prompts/insert_includes_LLM.prompt +262 -252
- pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
- pdd/prompts/prompt_diff_LLM.prompt +82 -0
- pdd/remote_session.py +876 -0
- pdd/server/__init__.py +52 -0
- pdd/server/app.py +335 -0
- pdd/server/click_executor.py +587 -0
- pdd/server/executor.py +338 -0
- pdd/server/jobs.py +661 -0
- pdd/server/models.py +241 -0
- pdd/server/routes/__init__.py +31 -0
- pdd/server/routes/architecture.py +451 -0
- pdd/server/routes/auth.py +364 -0
- pdd/server/routes/commands.py +929 -0
- pdd/server/routes/config.py +42 -0
- pdd/server/routes/files.py +603 -0
- pdd/server/routes/prompts.py +1347 -0
- pdd/server/routes/websocket.py +473 -0
- pdd/server/security.py +243 -0
- pdd/server/terminal_spawner.py +217 -0
- pdd/server/token_counter.py +222 -0
- pdd/summarize_directory.py +236 -237
- pdd/sync_animation.py +8 -4
- pdd/sync_determine_operation.py +329 -47
- pdd/sync_main.py +272 -28
- pdd/sync_orchestration.py +289 -211
- pdd/sync_order.py +304 -0
- pdd/template_expander.py +161 -0
- pdd/templates/architecture/architecture_json.prompt +41 -46
- pdd/trace.py +1 -1
- pdd/track_cost.py +0 -13
- pdd/unfinished_prompt.py +2 -1
- pdd/update_main.py +68 -26
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
- pdd_cli-0.0.121.dist-info/RECORD +229 -0
- pdd_cli-0.0.90.dist-info/RECORD +0 -153
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/generate_test.py
CHANGED
|
@@ -1,195 +1,256 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
3
|
-
|
|
1
|
+
"""
|
|
2
|
+
Module for generating unit tests from code or example files using LLMs.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from typing import Optional, Tuple
|
|
8
|
+
|
|
4
9
|
from rich.console import Console
|
|
5
|
-
from . import
|
|
6
|
-
|
|
7
|
-
from
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
11
|
-
from .postprocess import postprocess
|
|
10
|
+
from rich.markdown import Markdown
|
|
11
|
+
|
|
12
|
+
from pdd import DEFAULT_STRENGTH, DEFAULT_TIME, EXTRACTION_STRENGTH
|
|
13
|
+
from pdd.continue_generation import continue_generation
|
|
14
|
+
from pdd.llm_invoke import llm_invoke
|
|
15
|
+
from pdd.load_prompt_template import load_prompt_template
|
|
16
|
+
from pdd.postprocess import postprocess
|
|
17
|
+
from pdd.preprocess import preprocess
|
|
18
|
+
from pdd.unfinished_prompt import unfinished_prompt
|
|
12
19
|
|
|
13
20
|
console = Console()
|
|
14
21
|
|
|
22
|
+
|
|
23
|
+
def _validate_inputs(
|
|
24
|
+
prompt: str,
|
|
25
|
+
code: Optional[str],
|
|
26
|
+
strength: float,
|
|
27
|
+
temperature: float,
|
|
28
|
+
language: str
|
|
29
|
+
) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Validates the inputs for generate_test function.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If any input is invalid.
|
|
35
|
+
"""
|
|
36
|
+
if not isinstance(prompt, str) or not prompt.strip():
|
|
37
|
+
raise ValueError("Prompt must be a non-empty string")
|
|
38
|
+
|
|
39
|
+
if code is None or not isinstance(code, str) or not code.strip():
|
|
40
|
+
raise ValueError("Code must be a non-empty string")
|
|
41
|
+
|
|
42
|
+
if not isinstance(strength, (int, float)) or not 0 <= strength <= 1:
|
|
43
|
+
raise ValueError("Strength must be a float between 0 and 1")
|
|
44
|
+
|
|
45
|
+
if not isinstance(temperature, (int, float)):
|
|
46
|
+
raise ValueError("Temperature must be a float")
|
|
47
|
+
|
|
48
|
+
if not isinstance(language, str) or not language.strip():
|
|
49
|
+
raise ValueError("Language must be a non-empty string")
|
|
50
|
+
|
|
51
|
+
|
|
15
52
|
def generate_test(
|
|
16
53
|
prompt: str,
|
|
17
|
-
code: str,
|
|
54
|
+
code: Optional[str] = None,
|
|
55
|
+
example: Optional[str] = None,
|
|
18
56
|
strength: float = DEFAULT_STRENGTH,
|
|
19
57
|
temperature: float = 0.0,
|
|
20
58
|
time: float = DEFAULT_TIME,
|
|
21
|
-
language: str =
|
|
59
|
+
language: str = 'python',
|
|
22
60
|
verbose: bool = False,
|
|
23
61
|
source_file_path: Optional[str] = None,
|
|
24
62
|
test_file_path: Optional[str] = None,
|
|
25
|
-
module_name: Optional[str] = None
|
|
63
|
+
module_name: Optional[str] = None,
|
|
64
|
+
existing_tests: Optional[str] = None
|
|
26
65
|
) -> Tuple[str, float, str]:
|
|
27
66
|
"""
|
|
28
|
-
|
|
67
|
+
Generates a unit test for a given code file or example usage using an LLM.
|
|
29
68
|
|
|
30
69
|
Args:
|
|
31
|
-
prompt
|
|
32
|
-
code
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
time
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
70
|
+
prompt: The prompt that generated the code (context).
|
|
71
|
+
code: The source code to test. Mutually exclusive with 'example'.
|
|
72
|
+
example: An example usage of the module. Mutually exclusive with 'code'.
|
|
73
|
+
strength: LLM strength (0.0 to 1.0).
|
|
74
|
+
temperature: LLM temperature.
|
|
75
|
+
time: Thinking effort for the LLM (0.0 to 1.0).
|
|
76
|
+
language: Target language for the test.
|
|
77
|
+
verbose: Whether to print detailed logs.
|
|
78
|
+
source_file_path: Path to the code under test.
|
|
79
|
+
test_file_path: Destination path for the test.
|
|
80
|
+
module_name: Name of the module for imports.
|
|
81
|
+
existing_tests: Content of existing tests to merge.
|
|
41
82
|
|
|
42
83
|
Returns:
|
|
43
|
-
Tuple
|
|
84
|
+
Tuple containing:
|
|
85
|
+
- unit_test (str): The generated test code.
|
|
86
|
+
- total_cost (float): Total cost of generation.
|
|
87
|
+
- model_name (str): Name of the model used.
|
|
44
88
|
"""
|
|
45
89
|
total_cost = 0.0
|
|
46
|
-
model_name = ""
|
|
90
|
+
model_name = "unknown"
|
|
47
91
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if not template:
|
|
52
|
-
raise ValueError("Failed to load generate_test_LLM prompt template")
|
|
53
|
-
|
|
54
|
-
# Step 2: Preprocess template
|
|
55
|
-
processed_template = preprocess(template, recursive=False, double_curly_brackets=False)
|
|
56
|
-
processed_prompt = preprocess(prompt, recursive=False, double_curly_brackets=False)
|
|
57
|
-
|
|
58
|
-
# Step 3: Run through LLM
|
|
59
|
-
input_json = {
|
|
60
|
-
"prompt_that_generated_code": processed_prompt,
|
|
61
|
-
"code": code,
|
|
62
|
-
"language": language,
|
|
63
|
-
"source_file_path": source_file_path or "",
|
|
64
|
-
"test_file_path": test_file_path or "",
|
|
65
|
-
"module_name": module_name or ""
|
|
66
|
-
}
|
|
92
|
+
# --- Step 1: Determine prompt template and validate inputs ---
|
|
93
|
+
if (code is None and example is None) or (code is not None and example is not None):
|
|
94
|
+
raise ValueError("Exactly one of 'code' or 'example' must be provided.")
|
|
67
95
|
|
|
68
|
-
|
|
69
|
-
|
|
96
|
+
template_name = "generate_test_from_example_LLM" if example else "generate_test_LLM"
|
|
97
|
+
|
|
98
|
+
raw_template = load_prompt_template(template_name)
|
|
99
|
+
if not raw_template:
|
|
100
|
+
raise ValueError(f"Failed to load {template_name} prompt template")
|
|
101
|
+
|
|
102
|
+
# --- Step 2: Preprocess template and prompt ---
|
|
103
|
+
# Preprocess the template
|
|
104
|
+
prompt_template = preprocess(
|
|
105
|
+
raw_template,
|
|
106
|
+
recursive=False,
|
|
107
|
+
double_curly_brackets=False
|
|
108
|
+
)
|
|
70
109
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
110
|
+
# Preprocess the original prompt input
|
|
111
|
+
processed_prompt_input = preprocess(
|
|
112
|
+
prompt,
|
|
113
|
+
recursive=False,
|
|
114
|
+
double_curly_brackets=False
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# --- Step 3: Run inputs through LLM ---
|
|
118
|
+
input_data = {
|
|
119
|
+
"prompt_that_generated_code": processed_prompt_input,
|
|
120
|
+
"language": language,
|
|
121
|
+
"source_file_path": source_file_path if source_file_path else "",
|
|
122
|
+
"test_file_path": test_file_path if test_file_path else "",
|
|
123
|
+
"module_name": module_name if module_name else "",
|
|
124
|
+
"existing_tests": existing_tests if existing_tests else ""
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if example:
|
|
128
|
+
input_data["example"] = example
|
|
129
|
+
else:
|
|
130
|
+
input_data["code"] = code
|
|
131
|
+
|
|
132
|
+
if verbose:
|
|
133
|
+
console.print(
|
|
134
|
+
f"[bold blue]Generating unit test using template: {template_name}[/bold blue]"
|
|
135
|
+
)
|
|
136
|
+
console.print(f"[dim]Strength: {strength}, Time: {time}, Temp: {temperature}[/dim]")
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
llm_result = llm_invoke(
|
|
140
|
+
prompt=prompt_template,
|
|
141
|
+
input_json=input_data,
|
|
74
142
|
strength=strength,
|
|
75
143
|
temperature=temperature,
|
|
76
144
|
time=time,
|
|
77
145
|
verbose=verbose
|
|
78
146
|
)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
console.print(f"[bold red]Error invoking LLM:[/bold red] {e}")
|
|
149
|
+
raise
|
|
79
150
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
151
|
+
current_text = llm_result['result']
|
|
152
|
+
total_cost += llm_result.get('cost', 0.0)
|
|
153
|
+
model_name = llm_result.get('model_name', 'unknown')
|
|
83
154
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
155
|
+
# --- Step 4: Verbose Output of Initial Result ---
|
|
156
|
+
if verbose:
|
|
157
|
+
console.print("[bold green]Initial LLM Output:[/bold green]")
|
|
158
|
+
console.print(Markdown(current_text))
|
|
159
|
+
console.print(f"[dim]Initial Cost: ${llm_result.get('cost', 0.0):.6f}[/dim]")
|
|
87
160
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# If the tail is empty, assume generation is complete
|
|
98
|
-
if verbose:
|
|
99
|
-
console.print("[bold yellow]Last 600 chars are empty, assuming generation is complete[/bold yellow]")
|
|
100
|
-
reasoning = "Generation appears complete (tail is empty)"
|
|
101
|
-
is_finished = True
|
|
102
|
-
check_cost = 0.0
|
|
103
|
-
check_model = model_name
|
|
104
|
-
else:
|
|
105
|
-
reasoning, is_finished, check_cost, check_model = unfinished_prompt(
|
|
106
|
-
prompt_text=last_600_chars,
|
|
107
|
-
strength=strength,
|
|
108
|
-
temperature=temperature,
|
|
109
|
-
time=time,
|
|
110
|
-
language=language,
|
|
111
|
-
verbose=verbose
|
|
112
|
-
)
|
|
113
|
-
total_cost += check_cost
|
|
114
|
-
|
|
115
|
-
if not is_finished:
|
|
116
|
-
if verbose:
|
|
117
|
-
console.print("[bold yellow]Generation incomplete. Continuing...[/bold yellow]")
|
|
118
|
-
|
|
119
|
-
continued_result, continue_cost, continue_model = continue_generation(
|
|
120
|
-
formatted_input_prompt=processed_template,
|
|
121
|
-
llm_output=result,
|
|
161
|
+
# --- Step 5: Detect incomplete generation ---
|
|
162
|
+
# Check the last 600 characters
|
|
163
|
+
last_chunk = current_text[-600:] if len(current_text) > 600 else current_text
|
|
164
|
+
|
|
165
|
+
# Only check if there is actual content
|
|
166
|
+
if last_chunk.strip():
|
|
167
|
+
try:
|
|
168
|
+
reasoning, is_finished, check_cost, _ = unfinished_prompt(
|
|
169
|
+
prompt_text=last_chunk,
|
|
122
170
|
strength=strength,
|
|
123
171
|
temperature=temperature,
|
|
124
172
|
time=time,
|
|
125
173
|
language=language,
|
|
126
174
|
verbose=verbose
|
|
127
175
|
)
|
|
128
|
-
total_cost +=
|
|
129
|
-
result = continued_result
|
|
130
|
-
model_name = continue_model
|
|
176
|
+
total_cost += check_cost
|
|
131
177
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
178
|
+
if not is_finished:
|
|
179
|
+
if verbose:
|
|
180
|
+
console.print(
|
|
181
|
+
"[yellow]Output detected as incomplete. Continuing generation...[/yellow]"
|
|
182
|
+
)
|
|
183
|
+
console.print(f"[dim]Reasoning: {reasoning}[/dim]")
|
|
184
|
+
|
|
185
|
+
# We need the formatted prompt for continue_generation.
|
|
186
|
+
# Since llm_invoke handles formatting internally, we attempt to format here
|
|
187
|
+
# to pass context to the continuation logic.
|
|
188
|
+
try:
|
|
189
|
+
formatted_input_prompt = prompt_template.format(**input_data)
|
|
190
|
+
except Exception:
|
|
191
|
+
# Fallback if simple formatting fails (e.g. complex jinja or missing keys)
|
|
192
|
+
# We use the raw template as best effort context
|
|
193
|
+
formatted_input_prompt = prompt_template
|
|
194
|
+
|
|
195
|
+
final_llm_output, cont_cost, cont_model = continue_generation(
|
|
196
|
+
formatted_input_prompt=formatted_input_prompt,
|
|
197
|
+
llm_output=current_text,
|
|
198
|
+
strength=strength,
|
|
199
|
+
temperature=temperature,
|
|
200
|
+
verbose=verbose
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
current_text = final_llm_output
|
|
204
|
+
total_cost += cont_cost
|
|
205
|
+
model_name = cont_model # Update to the model used for continuation
|
|
143
206
|
except Exception as e:
|
|
144
|
-
console.print(f"[bold red]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
processed_result = code_blocks[0].strip() if code_blocks else result
|
|
159
|
-
else:
|
|
160
|
-
# No code blocks found, use raw result
|
|
161
|
-
processed_result = result
|
|
162
|
-
|
|
163
|
-
post_cost = 0.0
|
|
164
|
-
|
|
165
|
-
# Step 5: Print total cost if verbose
|
|
207
|
+
console.print(f"[bold red]Error during completion check/continuation:[/bold red] {e}")
|
|
208
|
+
# Proceed with what we have if check fails
|
|
209
|
+
|
|
210
|
+
# --- Step 6: Postprocess ---
|
|
211
|
+
try:
|
|
212
|
+
extracted_code, pp_cost, _ = postprocess(
|
|
213
|
+
llm_output=current_text,
|
|
214
|
+
language=language,
|
|
215
|
+
strength=EXTRACTION_STRENGTH,
|
|
216
|
+
verbose=verbose
|
|
217
|
+
)
|
|
218
|
+
total_cost += pp_cost
|
|
219
|
+
unit_test = extracted_code
|
|
220
|
+
except Exception as e:
|
|
166
221
|
if verbose:
|
|
167
|
-
console.print(f"[bold
|
|
168
|
-
|
|
222
|
+
console.print(f"[bold red]Postprocessing failed:[/bold red] {e}")
|
|
223
|
+
unit_test = ""
|
|
169
224
|
|
|
170
|
-
|
|
171
|
-
|
|
225
|
+
# Fallback extraction if postprocess returned empty or failed
|
|
226
|
+
if not unit_test.strip():
|
|
227
|
+
if verbose:
|
|
228
|
+
console.print(
|
|
229
|
+
"[yellow]Postprocess returned empty. Attempting fallback regex extraction.[/yellow]"
|
|
230
|
+
)
|
|
172
231
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
232
|
+
# Regex to find code blocks, preferring those with specific keywords
|
|
233
|
+
code_block_pattern = re.compile(r"```(?:\w+)?\n(.*?)```", re.DOTALL)
|
|
234
|
+
matches = code_block_pattern.findall(current_text)
|
|
176
235
|
|
|
236
|
+
best_match = ""
|
|
237
|
+
for match in matches:
|
|
238
|
+
# Heuristic: prefer blocks that look like tests
|
|
239
|
+
if "def test_" in match or "import unittest" in match or "import pytest" in match:
|
|
240
|
+
best_match = match
|
|
241
|
+
break
|
|
177
242
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if not isinstance(temperature, float):
|
|
193
|
-
raise ValueError("Temperature must be a float")
|
|
194
|
-
if not language or not isinstance(language, str):
|
|
195
|
-
raise ValueError("Language must be a non-empty string")
|
|
243
|
+
if not best_match and matches:
|
|
244
|
+
# If no specific test keywords found, take the longest block
|
|
245
|
+
best_match = max(matches, key=len)
|
|
246
|
+
|
|
247
|
+
unit_test = best_match if best_match else current_text
|
|
248
|
+
|
|
249
|
+
# --- Step 7: Final Cost Reporting ---
|
|
250
|
+
if verbose:
|
|
251
|
+
console.print(f"[bold blue]Generation Complete.[/bold blue]")
|
|
252
|
+
console.print(f"[bold]Total Cost:[/bold] ${total_cost:.6f}")
|
|
253
|
+
console.print(f"[dim]Final Model: {model_name}[/dim]")
|
|
254
|
+
|
|
255
|
+
# --- Step 8: Return ---
|
|
256
|
+
return unit_test, total_cost, model_name
|
pdd/get_comment.py
CHANGED
|
@@ -1,55 +1,30 @@
|
|
|
1
|
-
# To achieve the task of writing a Python function `get_comment` that returns the comment character(s) associated with a given programming language, we need to follow the steps outlined in your description. Here's how you can implement this:
|
|
2
|
-
|
|
3
|
-
# ```python
|
|
4
|
-
import os
|
|
5
1
|
import csv
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
from pdd.path_resolution import get_default_resolver
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_comment(language: str) -> str:
|
|
7
|
+
try:
|
|
8
|
+
resolver = get_default_resolver()
|
|
9
|
+
csv_file_path = resolver.resolve_data_file("data/language_format.csv")
|
|
10
|
+
except ValueError:
|
|
11
|
+
return "del"
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
if not isinstance(language, str):
|
|
14
|
+
return "del"
|
|
14
15
|
|
|
15
|
-
# Step 2: Lower case the language string
|
|
16
16
|
language = language.lower()
|
|
17
17
|
|
|
18
18
|
try:
|
|
19
|
-
|
|
20
|
-
with open(csv_file_path, mode='r', newline='') as csvfile:
|
|
19
|
+
with open(str(csv_file_path), mode="r", newline="") as csvfile:
|
|
21
20
|
reader = csv.DictReader(csvfile)
|
|
22
21
|
for row in reader:
|
|
23
|
-
if row[
|
|
24
|
-
comment = row
|
|
25
|
-
|
|
26
|
-
if comment:
|
|
27
|
-
return comment
|
|
28
|
-
else:
|
|
29
|
-
return 'del'
|
|
22
|
+
if row["language"].lower() == language:
|
|
23
|
+
comment = row.get("comment", "")
|
|
24
|
+
return comment if comment else "del"
|
|
30
25
|
except FileNotFoundError:
|
|
31
|
-
return
|
|
32
|
-
except Exception
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
return 'del' # Return 'del' if the language is not found
|
|
36
|
-
|
|
37
|
-
# Example usage:
|
|
38
|
-
# Assuming the environment variable PDD_PATH is set correctly
|
|
39
|
-
# print(get_comment('Python')) # Output: #
|
|
40
|
-
# print(get_comment('Java')) # Output: //
|
|
41
|
-
# ```
|
|
42
|
-
|
|
43
|
-
# ### Explanation:
|
|
44
|
-
|
|
45
|
-
# 1. **Environment Variable**: The function first retrieves the `PDD_PATH` environment variable to locate the CSV file. If the environment variable is not set, it returns `'del'`.
|
|
46
|
-
|
|
47
|
-
# 2. **Case Insensitivity**: The input language string is converted to lowercase to ensure case-insensitive comparison.
|
|
48
|
-
|
|
49
|
-
# 3. **CSV Reading**: The function reads the CSV file using Python's `csv.DictReader`, which allows accessing each row as a dictionary. It checks if the lowercase version of the language matches any entry in the CSV.
|
|
50
|
-
|
|
51
|
-
# 4. **Validation**: If a match is found, it checks if the comment character(s) is valid (i.e., not an empty string). If valid, it returns the comment character(s); otherwise, it returns `'del'`.
|
|
52
|
-
|
|
53
|
-
# 5. **Error Handling**: The function handles potential errors such as file not found or other exceptions by returning `'del'`.
|
|
26
|
+
return "del"
|
|
27
|
+
except Exception:
|
|
28
|
+
return "del"
|
|
54
29
|
|
|
55
|
-
|
|
30
|
+
return "del"
|
pdd/get_extension.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Module to retrieve file extensions for programming languages."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import pandas as pd
|
|
4
|
+
from pdd.path_resolution import get_default_resolver
|
|
5
5
|
|
|
6
6
|
def get_extension(language: str) -> str:
|
|
7
7
|
"""
|
|
@@ -18,13 +18,12 @@ def get_extension(language: str) -> str:
|
|
|
18
18
|
ValueError: If the PDD_PATH environment variable is not set.
|
|
19
19
|
FileNotFoundError: If the language_format.csv file is not found.
|
|
20
20
|
"""
|
|
21
|
-
# Step 1:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
csv_file_path = os.path.join(pdd_path, 'data', 'language_format.csv')
|
|
21
|
+
# Step 1: Resolve CSV path from PDD_PATH
|
|
22
|
+
resolver = get_default_resolver()
|
|
23
|
+
try:
|
|
24
|
+
csv_file_path = resolver.resolve_data_file("data/language_format.csv")
|
|
25
|
+
except ValueError as exc:
|
|
26
|
+
raise ValueError("Environment variable PDD_PATH is not set.") from exc
|
|
28
27
|
|
|
29
28
|
# Step 2: Lower case the language string
|
|
30
29
|
language_lower = language.lower()
|
|
@@ -63,4 +62,4 @@ def get_extension(language: str) -> str:
|
|
|
63
62
|
# ```bash
|
|
64
63
|
# pip install pandas
|
|
65
64
|
# ```
|
|
66
|
-
# - Ensure that the CSV file is structured correctly and located at the specified path.
|
|
65
|
+
# - Ensure that the CSV file is structured correctly and located at the specified path.
|