pdd-cli 0.0.90__py3-none-any.whl → 0.0.118__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.
Files changed (144) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +497 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +526 -0
  6. pdd/agentic_common.py +521 -786
  7. pdd/agentic_e2e_fix.py +319 -0
  8. pdd/agentic_e2e_fix_orchestrator.py +426 -0
  9. pdd/agentic_fix.py +118 -3
  10. pdd/agentic_update.py +25 -8
  11. pdd/architecture_sync.py +565 -0
  12. pdd/auth_service.py +210 -0
  13. pdd/auto_deps_main.py +63 -53
  14. pdd/auto_include.py +185 -3
  15. pdd/auto_update.py +125 -47
  16. pdd/bug_main.py +195 -23
  17. pdd/cmd_test_main.py +345 -197
  18. pdd/code_generator.py +4 -2
  19. pdd/code_generator_main.py +118 -32
  20. pdd/commands/__init__.py +6 -0
  21. pdd/commands/analysis.py +87 -29
  22. pdd/commands/auth.py +309 -0
  23. pdd/commands/connect.py +290 -0
  24. pdd/commands/fix.py +136 -113
  25. pdd/commands/maintenance.py +3 -2
  26. pdd/commands/misc.py +8 -0
  27. pdd/commands/modify.py +190 -164
  28. pdd/commands/sessions.py +284 -0
  29. pdd/construct_paths.py +334 -32
  30. pdd/context_generator_main.py +167 -170
  31. pdd/continue_generation.py +6 -3
  32. pdd/core/__init__.py +33 -0
  33. pdd/core/cli.py +27 -3
  34. pdd/core/cloud.py +237 -0
  35. pdd/core/errors.py +4 -0
  36. pdd/core/remote_session.py +61 -0
  37. pdd/crash_main.py +219 -23
  38. pdd/data/llm_model.csv +4 -4
  39. pdd/docs/prompting_guide.md +864 -0
  40. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  41. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  42. pdd/fix_code_loop.py +208 -34
  43. pdd/fix_code_module_errors.py +6 -2
  44. pdd/fix_error_loop.py +291 -38
  45. pdd/fix_main.py +204 -4
  46. pdd/fix_verification_errors_loop.py +235 -26
  47. pdd/fix_verification_main.py +269 -83
  48. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  49. pdd/frontend/dist/assets/index-DQ3wkeQ2.js +449 -0
  50. pdd/frontend/dist/index.html +376 -0
  51. pdd/frontend/dist/logo.svg +33 -0
  52. pdd/generate_output_paths.py +46 -5
  53. pdd/generate_test.py +212 -151
  54. pdd/get_comment.py +19 -44
  55. pdd/get_extension.py +8 -9
  56. pdd/get_jwt_token.py +309 -20
  57. pdd/get_language.py +8 -7
  58. pdd/get_run_command.py +7 -5
  59. pdd/insert_includes.py +2 -1
  60. pdd/llm_invoke.py +459 -95
  61. pdd/load_prompt_template.py +15 -34
  62. pdd/path_resolution.py +140 -0
  63. pdd/postprocess.py +4 -1
  64. pdd/preprocess.py +68 -12
  65. pdd/preprocess_main.py +33 -1
  66. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  67. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  68. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  69. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  70. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  71. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  72. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  73. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  74. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  75. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  76. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  77. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  78. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +131 -0
  79. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  80. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  81. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  82. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  83. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  84. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  85. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  86. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  87. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  88. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  89. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  90. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  91. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  92. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  93. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  94. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  95. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  96. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  97. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  98. pdd/prompts/agentic_update_LLM.prompt +192 -338
  99. pdd/prompts/auto_include_LLM.prompt +22 -0
  100. pdd/prompts/change_LLM.prompt +3093 -1
  101. pdd/prompts/detect_change_LLM.prompt +571 -14
  102. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  103. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  104. pdd/prompts/generate_test_LLM.prompt +20 -1
  105. pdd/prompts/generate_test_from_example_LLM.prompt +115 -0
  106. pdd/prompts/insert_includes_LLM.prompt +262 -252
  107. pdd/prompts/prompt_code_diff_LLM.prompt +119 -0
  108. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  109. pdd/remote_session.py +876 -0
  110. pdd/server/__init__.py +52 -0
  111. pdd/server/app.py +335 -0
  112. pdd/server/click_executor.py +587 -0
  113. pdd/server/executor.py +338 -0
  114. pdd/server/jobs.py +661 -0
  115. pdd/server/models.py +241 -0
  116. pdd/server/routes/__init__.py +31 -0
  117. pdd/server/routes/architecture.py +451 -0
  118. pdd/server/routes/auth.py +364 -0
  119. pdd/server/routes/commands.py +929 -0
  120. pdd/server/routes/config.py +42 -0
  121. pdd/server/routes/files.py +603 -0
  122. pdd/server/routes/prompts.py +1322 -0
  123. pdd/server/routes/websocket.py +473 -0
  124. pdd/server/security.py +243 -0
  125. pdd/server/terminal_spawner.py +209 -0
  126. pdd/server/token_counter.py +222 -0
  127. pdd/summarize_directory.py +236 -237
  128. pdd/sync_animation.py +8 -4
  129. pdd/sync_determine_operation.py +329 -47
  130. pdd/sync_main.py +272 -28
  131. pdd/sync_orchestration.py +136 -75
  132. pdd/template_expander.py +161 -0
  133. pdd/templates/architecture/architecture_json.prompt +41 -46
  134. pdd/trace.py +1 -1
  135. pdd/track_cost.py +0 -13
  136. pdd/unfinished_prompt.py +2 -1
  137. pdd/update_main.py +23 -5
  138. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/METADATA +15 -10
  139. pdd_cli-0.0.118.dist-info/RECORD +227 -0
  140. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  141. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/WHEEL +0 -0
  142. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/entry_points.txt +0 -0
  143. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/licenses/LICENSE +0 -0
  144. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/top_level.txt +0 -0
pdd/generate_test.py CHANGED
@@ -1,195 +1,256 @@
1
- from typing import Tuple, Optional
2
- from rich import print
3
- from rich.markdown import Markdown
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 EXTRACTION_STRENGTH, DEFAULT_STRENGTH, DEFAULT_TIME
6
- from .load_prompt_template import load_prompt_template
7
- from .preprocess import preprocess
8
- from .llm_invoke import llm_invoke
9
- from .unfinished_prompt import unfinished_prompt
10
- from .continue_generation import continue_generation
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 = "python",
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
- Generate a unit test from a code file using LLM.
67
+ Generates a unit test for a given code file or example usage using an LLM.
29
68
 
30
69
  Args:
31
- prompt (str): The prompt that generated the code file.
32
- code (str): The code to generate a unit test from.
33
- strength (float): The strength of the LLM model (0-1).
34
- temperature (float): The temperature of the LLM model.
35
- language (str): The programming language for the unit test.
36
- time (float, optional): Time budget for LLM calls. Defaults to DEFAULT_TIME.
37
- verbose (bool): Whether to print detailed information.
38
- source_file_path (Optional[str]): Absolute or relative path to the code under test.
39
- test_file_path (Optional[str]): Destination path for the generated test file.
40
- module_name (Optional[str]): Module name (without extension) for proper imports.
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[str, float, str]: (unit_test, total_cost, model_name)
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
- try:
49
- # Step 1: Load prompt template
50
- template = load_prompt_template("generate_test_LLM")
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
- if verbose:
69
- console.print("[bold blue]Generating unit test...[/bold blue]")
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
- response = llm_invoke(
72
- prompt=processed_template,
73
- input_json=input_json,
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
- total_cost += response['cost']
81
- model_name = response['model_name']
82
- result = response['result']
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
- # Validate that we got a non-empty result
85
- if not result or not result.strip():
86
- raise ValueError(f"LLM test generation returned empty result. Model: {model_name}, Cost: ${response['cost']:.6f}")
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
- if verbose:
89
- console.print(Markdown(result))
90
- console.print(f"[bold green]Initial generation cost: ${total_cost:.6f}[/bold green]")
91
-
92
- # Step 4: Check if generation is complete
93
- last_600_chars = result[-600:] if len(result) > 600 else result
94
-
95
- # Validate that the last_600_chars is not empty after stripping
96
- if not last_600_chars.strip():
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 += continue_cost
129
- result = continued_result
130
- model_name = continue_model
176
+ total_cost += check_cost
131
177
 
132
- # Process the final result
133
- try:
134
- processed_result, post_cost, post_model = postprocess(
135
- result,
136
- language=language,
137
- strength=EXTRACTION_STRENGTH,
138
- temperature=temperature,
139
- time=time,
140
- verbose=verbose
141
- )
142
- total_cost += post_cost
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]Postprocess failed: {str(e)}[/bold red]")
145
- console.print(f"[bold yellow]Falling back to raw result[/bold yellow]")
146
-
147
- # Try to extract code blocks directly from the raw result
148
- import re
149
- code_blocks = re.findall(r'```(?:python)?\s*(.*?)```', result, re.DOTALL | re.IGNORECASE)
150
-
151
- if code_blocks:
152
- # Use the first substantial code block
153
- for block in code_blocks:
154
- if len(block.strip()) > 100 and ('def test_' in block or 'import' in block):
155
- processed_result = block.strip()
156
- break
157
- else:
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 green]Total cost: ${total_cost:.6f}[/bold green]")
168
- console.print(f"[bold blue]Final model used: {model_name}[/bold blue]")
222
+ console.print(f"[bold red]Postprocessing failed:[/bold red] {e}")
223
+ unit_test = ""
169
224
 
170
- # Step 6: Return results
171
- return processed_result, total_cost, model_name
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
- except Exception as e:
174
- console.print(f"[bold red]Error: {str(e)}[/bold red]")
175
- raise
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
- def _validate_inputs(
179
- prompt: str,
180
- code: str,
181
- strength: float,
182
- temperature: float,
183
- language: str
184
- ) -> None:
185
- """Validate input parameters."""
186
- if not prompt or not isinstance(prompt, str):
187
- raise ValueError("Prompt must be a non-empty string")
188
- if not code or not isinstance(code, str):
189
- raise ValueError("Code must be a non-empty string")
190
- if not isinstance(strength, float) or not 0 <= strength <= 1:
191
- raise ValueError("Strength must be a float between 0 and 1")
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
- def get_comment(language):
8
- # Step 1: Load environment variables to get the path to the CSV file
9
- pdd_path = os.getenv('PDD_PATH')
10
- if not pdd_path:
11
- return 'del' # Return 'del' if the environment variable is not set
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
- csv_file_path = os.path.join(pdd_path, 'data', 'language_format.csv')
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
- # Step 3: Open the CSV file and look up the comment character(s)
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['language'].lower() == language:
24
- comment = row['comment']
25
- # Step 4: Check if the comment character(s) is valid
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 'del' # Return 'del' if the file is not found
32
- except Exception as e:
33
- return 'del' # Return 'del' for any other exceptions
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
- # This implementation assumes that the CSV file is correctly formatted and that the environment variable `PDD_PATH` is set to the correct path.
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: Load the environment variable PDD_PATH
22
- pdd_path = os.getenv('PDD_PATH')
23
- if not pdd_path:
24
- raise ValueError("Environment variable PDD_PATH is not set.")
25
-
26
- # Construct the full path to the CSV file
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.