Skip to content

Commit cea3224

Browse files
committed
Prepare implementation scripts
1 parent f0d65e2 commit cea3224

10 files changed

Lines changed: 147 additions & 2 deletions

codeplain_REST_api.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,36 @@ def connection_check(self, client_version):
143143
}
144144
return self.post_request(endpoint_url, headers, payload, None, num_retries=0, silent=True)
145145

146+
def prepare_implementation(
147+
self,
148+
frid: str,
149+
plain_source_tree: dict,
150+
linked_resources: dict,
151+
existing_files_content: dict,
152+
memory_files_content: dict,
153+
module_name: str,
154+
required_modules: dict,
155+
include_unittests: bool,
156+
run_state: RunState,
157+
prepare_implementation_script: str,
158+
) -> dict:
159+
endpoint_url = f"{self.api_url}/prepare_implementation"
160+
headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
161+
162+
payload = {
163+
"frid": frid,
164+
"plain_source_tree": plain_source_tree,
165+
"linked_resources": linked_resources,
166+
"existing_files_content": existing_files_content,
167+
"memory_files_content": memory_files_content,
168+
"module_name": module_name,
169+
"required_modules": required_modules,
170+
"include_unittests": include_unittests,
171+
"prepare_implementation_script": prepare_implementation_script,
172+
}
173+
174+
return self.post_request(endpoint_url, headers, payload, run_state)
175+
146176
def render_functional_requirement(
147177
self,
148178
frid: str,
@@ -154,6 +184,7 @@ def render_functional_requirement(
154184
required_modules: dict,
155185
include_unittests: bool,
156186
run_state: RunState,
187+
implementation_information: Optional[str] = None,
157188
) -> dict[str, str]:
158189
"""
159190
Renders the content of a functionality based on the provided ID,
@@ -195,6 +226,9 @@ def render_functional_requirement(
195226
"include_unittests": include_unittests,
196227
}
197228

229+
if implementation_information is not None:
230+
payload["implementation_information"] = implementation_information
231+
198232
return self.post_request(endpoint_url, headers, payload, run_state)
199233

200234
def fix_unittests_issue(

module_renderer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def _build_render_context_for_module(
173173
unittests_script=self.args.unittests_script,
174174
conformance_tests_script=self.args.conformance_tests_script,
175175
prepare_environment_script=self.args.prepare_environment_script,
176+
prepare_implementation_script=self.args.prepare_implementation_script,
176177
copy_build=self.args.copy_build,
177178
copy_conformance_tests=self.args.copy_conformance_tests,
178179
render_range=render_range,

plain2code_arguments.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CONFORMANCE_TESTS_SCRIPT_NAME = "conformance_tests_script"
1919
DEFAULT_LOG_FILE_NAME = "codeplain.log"
2020
PREPARE_ENVIRONMENT_SCRIPT_NAME = "prepare_environment_script"
21+
PREPARE_IMPLEMENTATION_SCRIPT_NAME = "prepare_implementation_script"
2122

2223

2324
def process_test_script_path(script_arg_name, config):
@@ -252,6 +253,14 @@ def create_parser():
252253
help="Path to a shell script that prepares the testing environment. The script should accept the source code folder path as its first argument.",
253254
)
254255

256+
parser.add_argument(
257+
"--prepare-implementation-script",
258+
type=str,
259+
help="Path to a shell script that queries an LLM to produce additional implementation information. "
260+
"The script receives a path to a file containing LLM query instructions as its first argument "
261+
"and should print the LLM response to stdout.",
262+
)
263+
255264
parser.add_argument(
256265
"--test-script-timeout",
257266
type=int,
@@ -371,7 +380,7 @@ def parse_arguments():
371380
if args.full_plain and args.dry_run:
372381
parser.error("--full-plain and --dry-run are mutually exclusive")
373382

374-
script_arg_names = [UNIT_TESTS_SCRIPT_NAME, CONFORMANCE_TESTS_SCRIPT_NAME, PREPARE_ENVIRONMENT_SCRIPT_NAME]
383+
script_arg_names = [UNIT_TESTS_SCRIPT_NAME, CONFORMANCE_TESTS_SCRIPT_NAME, PREPARE_ENVIRONMENT_SCRIPT_NAME, PREPARE_IMPLEMENTATION_SCRIPT_NAME]
375384
for script_name in script_arg_names:
376385
args = process_test_script_path(script_name, args)
377386

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
import tempfile
3+
from typing import Any
4+
5+
import render_machine.render_utils as render_utils
6+
from memory_management import MemoryManager
7+
from plain2code_console import console
8+
from render_machine.actions.base_action import BaseAction
9+
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
10+
from render_machine.render_context import RenderContext
11+
from render_machine.render_types import RenderError
12+
13+
14+
class PrepareImplementationInformation(BaseAction):
15+
SUCCESSFUL_OUTCOME = "implementation_information_prepared"
16+
FAILED_OUTCOME = "implementation_information_preparation_failed"
17+
18+
def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
19+
if render_context.verbose:
20+
console.info(
21+
f"Running prepare_implementation_script {render_context.prepare_implementation_script} "
22+
f"for FRID {render_context.frid_context.frid}."
23+
)
24+
25+
_, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(render_context.build_folder)
26+
_, memory_files_content = MemoryManager.fetch_memory_files(render_context.memory_manager.memory_folder)
27+
28+
with open(render_context.prepare_implementation_script, "r", encoding="utf-8") as f:
29+
prepare_implementation_script_content = f.read()
30+
31+
api_response = render_context.codeplain_api.prepare_implementation(
32+
frid=render_context.frid_context.frid,
33+
plain_source_tree=render_context.plain_source_tree,
34+
linked_resources=render_context.frid_context.linked_resources,
35+
existing_files_content=existing_files_content,
36+
memory_files_content=memory_files_content,
37+
module_name=render_context.module_name,
38+
required_modules=render_context.get_required_modules_functionalities(),
39+
include_unittests=render_context.should_run_unit_tests(),
40+
run_state=render_context.run_state,
41+
prepare_implementation_script=prepare_implementation_script_content,
42+
)
43+
instructions = api_response.get("instructions", "")
44+
45+
tmp_path = None
46+
try:
47+
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding="utf-8") as tmp:
48+
tmp.write(instructions)
49+
tmp_path = tmp.name
50+
51+
exit_code, implementation_information, script_output_path = render_utils.execute_script(
52+
render_context.prepare_implementation_script,
53+
[tmp_path],
54+
render_context.verbose,
55+
"Prepare Implementation Information",
56+
timeout=render_context.test_script_timeout,
57+
stop_event=render_context.stop_event,
58+
)
59+
finally:
60+
if tmp_path and os.path.exists(tmp_path):
61+
os.unlink(tmp_path)
62+
63+
if exit_code == 0:
64+
render_context.frid_context.implementation_information = implementation_information
65+
render_context.script_execution_history.latest_prepare_implementation_output_path = script_output_path
66+
render_context.script_execution_history.should_update_script_outputs = True
67+
return self.SUCCESSFUL_OUTCOME, None
68+
else:
69+
return (
70+
self.FAILED_OUTCOME,
71+
RenderError.encode(
72+
message="Prepare implementation information failed. Please check the prepare_implementation_script.",
73+
error_type="PREPARE_IMPLEMENTATION_ERROR",
74+
exit_code=exit_code,
75+
script=render_context.prepare_implementation_script,
76+
).to_payload(),
77+
)

render_machine/actions/render_functional_requirement.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def execute(self, render_context: RenderContext, _previous_action_payload: Any |
5050
render_context.get_required_modules_functionalities(),
5151
render_context.should_run_unit_tests(),
5252
render_context.run_state,
53+
implementation_information=render_context.frid_context.implementation_information,
5354
)
5455
except FunctionalRequirementTooComplex as e:
5556
error_message = f"The functionality:\n{render_context.frid_context.functional_requirement_text}\n is too complex to be implemented. Please break down the functionality into smaller parts ({str(e)})."

render_machine/render_context.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def __init__(
4444
unittests_script: str,
4545
conformance_tests_script: str,
4646
prepare_environment_script: str,
47+
prepare_implementation_script: str,
4748
copy_build: bool,
4849
copy_conformance_tests: bool,
4950
render_range: list[str] | None,
@@ -69,6 +70,7 @@ def __init__(
6970
self.unittests_script = unittests_script
7071
self.conformance_tests_script = conformance_tests_script
7172
self.prepare_environment_script = prepare_environment_script
73+
self.prepare_implementation_script = prepare_implementation_script
7274
self.copy_build = copy_build
7375
self.copy_conformance_tests = copy_conformance_tests
7476
self.render_range = render_range
@@ -344,6 +346,10 @@ def start_refactoring_code(self):
344346
def finish_refactoring_code(self):
345347
pass
346348

349+
def start_prepare_implementation_information(self):
350+
if self.prepare_implementation_script is None:
351+
self.machine.dispatch(triggers.MARK_IMPLEMENTATION_INFORMATION_PREPARED)
352+
347353
def start_testing_environment_preparation(self):
348354
if (
349355
self.prepare_environment_script is None

render_machine/render_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class FridContext:
1313
functional_requirement_render_attempts: int = 0
1414
changed_files: set[str] = field(default_factory=set)
1515
refactoring_iteration: int = 0
16+
implementation_information: Optional[str] = None
1617

1718

1819
@dataclass
@@ -109,6 +110,7 @@ class ScriptExecutionHistory:
109110
latest_unit_test_output_path: Optional[str] = None
110111
latest_conformance_test_output_path: Optional[str] = None
111112
latest_testing_environment_output_path: Optional[str] = None
113+
latest_prepare_implementation_output_path: Optional[str] = None
112114
should_update_script_outputs: bool = False
113115

114116

render_machine/state_machine_config.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from render_machine.actions.fix_conformance_test import FixConformanceTest
2020
from render_machine.actions.fix_unit_tests import FixUnitTests
2121
from render_machine.actions.prepare_repositories import PrepareRepositories
22+
from render_machine.actions.prepare_implementation_information import PrepareImplementationInformation
2223
from render_machine.actions.prepare_testing_environment import PrepareTestingEnvironment
2324
from render_machine.actions.refactor_code import RefactorCode
2425
from render_machine.actions.render_conformance_tests import RenderConformanceTests
@@ -77,6 +78,7 @@ def get_action_map(self) -> Dict[str, Any]:
7778
"""Get the mapping of states to their corresponding actions."""
7879
return {
7980
States.RENDER_INITIALISED.value: PrepareRepositories(),
81+
f"{States.IMPLEMENTING_FRID.value}_{States.PREPARE_IMPLEMENTATION_INFORMATION.value}": PrepareImplementationInformation(),
8082
f"{States.IMPLEMENTING_FRID.value}_{States.READY_FOR_FRID_IMPLEMENTATION.value}": RenderFunctionalRequirement(),
8183
f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_UNIT_TESTS.value}_{States.UNIT_TESTS_READY.value}": RunUnitTests(),
8284
f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_UNIT_TESTS.value}_{States.UNIT_TESTS_FAILED.value}": FixUnitTests(),
@@ -112,6 +114,8 @@ def get_action_result_triggers_map(self) -> Dict[str, str]:
112114
"""Get the mapping of action outcomes to state machine triggers."""
113115
return {
114116
PrepareRepositories.SUCCESSFUL_OUTCOME: triggers.START_RENDER,
117+
PrepareImplementationInformation.SUCCESSFUL_OUTCOME: triggers.MARK_IMPLEMENTATION_INFORMATION_PREPARED,
118+
PrepareImplementationInformation.FAILED_OUTCOME: triggers.HANDLE_ERROR,
115119
RenderFunctionalRequirement.SUCCESSFUL_OUTCOME: triggers.RENDER_FUNCTIONAL_REQUIREMENT,
116120
RenderFunctionalRequirement.FUNCTIONAL_REQUIREMENT_TOO_COMPLEX_OUTCOME: triggers.HANDLE_ERROR,
117121
RunUnitTests.SUCCESSFUL_OUTCOME: triggers.MARK_UNIT_TESTS_PASSED,
@@ -221,11 +225,15 @@ def get_states(self, render_context: RenderContext) -> List[Any]:
221225
States.RENDER_INITIALISED.value,
222226
{
223227
"name": States.IMPLEMENTING_FRID.value,
224-
"initial": States.READY_FOR_FRID_IMPLEMENTATION.value,
228+
"initial": States.PREPARE_IMPLEMENTATION_INFORMATION.value,
225229
"on_enter": "start_implementing_frid",
226230
"on_exit": "finish_implementing_frid",
227231
"children": [
228232
{"name": States.STEP_COMPLETED.value, "on_exit": "finish_frid_implementation_step"},
233+
{
234+
"name": States.PREPARE_IMPLEMENTATION_INFORMATION.value,
235+
"on_enter": "start_prepare_implementation_information",
236+
},
229237
{"name": States.READY_FOR_FRID_IMPLEMENTATION.value, "on_enter": "check_frid_iteration_limit"},
230238
self.get_processing_unit_tests_states(UnitTestsConfig.for_implementation(render_context)),
231239
refactoring_code_states,
@@ -249,6 +257,11 @@ def get_transitions(self) -> List[Dict[str, Any]]:
249257
"trigger": triggers.START_RENDER,
250258
"dest": States.IMPLEMENTING_FRID.value,
251259
},
260+
{
261+
"source": f"{States.IMPLEMENTING_FRID.value}_{States.PREPARE_IMPLEMENTATION_INFORMATION.value}",
262+
"trigger": triggers.MARK_IMPLEMENTATION_INFORMATION_PREPARED,
263+
"dest": f"{States.IMPLEMENTING_FRID.value}_{States.READY_FOR_FRID_IMPLEMENTATION.value}",
264+
},
252265
{
253266
"source": f"{States.IMPLEMENTING_FRID.value}_{States.READY_FOR_FRID_IMPLEMENTATION.value}",
254267
"trigger": triggers.RENDER_FUNCTIONAL_REQUIREMENT,

render_machine/states.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class States(Enum):
2323
STEP_COMPLETED = "stepCompleted"
2424

2525
# FRID implementation states
26+
PREPARE_IMPLEMENTATION_INFORMATION = "prepareImplementationInformation"
2627
READY_FOR_FRID_IMPLEMENTATION = "readyForFridImplementation"
2728
FRID_FULLY_IMPLEMENTED = "fridFullyImplemented"
2829

render_machine/triggers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
START_NEW_REFACTORING_ITERATION = "start_new_refactoring_iteration"
2020
MARK_CONFORMANCE_TESTS_READY = "mark_conformance_tests_ready"
2121
MARK_TESTING_ENVIRONMENT_PREPARED = "mark_testing_environment_prepared"
22+
MARK_IMPLEMENTATION_INFORMATION_PREPARED = "mark_implementation_information_prepared"
2223
MARK_CONFORMANCE_TESTS_FAILED = "mark_conformance_tests_failed"
2324
MOVE_TO_NEXT_CONFORMANCE_TEST = "move_to_next_conformance_test"
2425
MARK_ALL_CONFORMANCE_TESTS_PASSED = "mark_all_conformance_tests_passed"

0 commit comments

Comments
 (0)