Skip to content

Commit 3a798c9

Browse files
authored
Merge pull request #33 from Codeplain-ai/feat/10-17-release
Migrate to new API keys
2 parents 43dff48 + 8333b2a commit 3a798c9

File tree

7 files changed

+93
-29
lines changed

7 files changed

+93
-29
lines changed

README.md

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,22 @@ To run the plain2code client, you need Python 3.11 or a later version.
3131

3232
**Windows users:** Please install WSL (Windows Subsystem for Linux) as this is currently the supported environment for running plain code on Windows.
3333

34-
#### Anthropic API Key
34+
#### Authorization - Codeplain API Key
3535

36-
For now you need to bring your own Anthropic API key to use Codeplain API. If you don't have Anthropic API key, you can create a free developer account at [console.anthropic.com](https://console.anthropic.com/). To experiment with Codeplain you need to top up your Anthropic account with $5-10.
36+
We are using Codeplain API Key to authorize requests to the Codeplain API. To get your Codeplain API Key, please contact Codeplain.ai support at [email protected].
3737

38-
With Anthropic API Key ready, please contact Codeplain.ai support at [email protected] to have the hash of your Anthropic API key added to the list of authorized API keys.
39-
40-
To have the hash generated use the following command:
41-
42-
`python hash_key.py $CLAUDE_API_KEY`
43-
44-
To set up your API key run:
38+
In order to generate code, you need to export the following environment variable:
4539

4640
```bash
47-
# Export API key directly
48-
export CLAUDE_API_KEY="your_actual_api_key_here"
41+
export CODEPLAIN_API_KEY="your_actual_api_key_here"
4942
```
5043

5144
### Installation Steps
5245

5346
1. Clone this repository
5447
2. Set your Codeplain API key as an environment variable:
5548
```
56-
export CLAUDE_API_KEY=your_api_key_here
49+
export CODEPLAIN_API_KEY=your_api_key_here
5750
```
5851
3. (Recommended) Create and activate a virtual environment:
5952
```bash

codeplain_REST_api.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def post_request(self, endpoint_url, headers, payload, run_state: Optional[RunSt
135135
f"Connection error: Unable to reach the Codeplain API at {self.api_url}. Please try again or contact support."
136136
)
137137

138-
def get_plain_source_tree(self, plain_source, loaded_templates):
138+
def get_plain_source_tree(self, plain_source, loaded_templates, run_state: RunState):
139139
"""
140140
Builds plain source tree from the given plain text source in Markdown format.
141141
@@ -154,7 +154,7 @@ def get_plain_source_tree(self, plain_source, loaded_templates):
154154

155155
payload = {"plain_source": plain_source, "loaded_templates": loaded_templates}
156156

157-
return self.post_request(endpoint_url, headers, payload, None)
157+
return self.post_request(endpoint_url, headers, payload, run_state)
158158

159159
def render_functional_requirement(
160160
self,
@@ -370,3 +370,13 @@ def analyze_rendering(
370370
}
371371

372372
return self.post_request(endpoint_url, headers, payload, run_state)
373+
374+
def finish_functional_requirement(self, frid, run_state: RunState):
375+
endpoint_url = f"{self.api_url}/finish_functional_requirement"
376+
headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
377+
378+
payload = {
379+
"frid": frid,
380+
}
381+
382+
return self.post_request(endpoint_url, headers, payload, run_state)

examples/example_hello_world_python/hello_world_python.plain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
***Acceptance Tests:***
1010

11-
- The App shouldn't show logging output in the console output (neither in stdout nor stderr).
1211
- The App should exit with status code 0 indicating successful execution.
12+
1313
- The App should complete execution in under 1 second.
1414

hash_key.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import sys
21
import hashlib
2+
import sys
3+
34

45
def hash_api_key(api_key):
56
"""Hash the provided API key using SHA-256 and return the hash as a hex string."""
@@ -11,17 +12,18 @@ def hash_api_key(api_key):
1112
error_message = f"An error occurred while hashing the API key: {str(e)}"
1213
raise Exception(error_message)
1314

15+
1416
if __name__ == "__main__":
1517
if len(sys.argv) != 2:
1618
print("Error: Exactly one argument must be provided for the API key.")
1719
print(f"Usage: python {sys.argv[0]} <api_key>")
1820
sys.exit(1)
19-
21+
2022
api_key = sys.argv[1]
21-
23+
2224
try:
2325
hashed_key = hash_api_key(api_key)
2426
print(f"Hashed API Key: {hashed_key}")
2527
except Exception as e:
2628
print(f"Error: {str(e)}")
27-
sys.exit(1)
29+
sys.exit(1)

plain2code.py

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from plain2code_arguments import parse_arguments
2020
from plain2code_console import console
2121
from plain2code_state import CONFORMANCE_TESTS_DEFINITION_FILE_NAME, ConformanceTestsUtils, ExecutionState, RunState
22+
# from plain2code_utils import AMBIGUITY_CAUSES, RetryOnlyFilter
2223
from plain2code_utils import RetryOnlyFilter
2324
from system_config import system_config
2425

@@ -33,7 +34,7 @@
3334
MAX_REFACTORING_ITERATIONS = 5
3435
MAX_UNIT_TEST_RENDER_RETRIES = 2
3536

36-
MAX_ISSUE_LENGTH = 15000 # Characters.
37+
MAX_ISSUE_LENGTH = 10000 # Characters.
3738

3839
UNRECOVERABLE_ERROR_EXIT_CODES = [69]
3940
TIMEOUT_ERROR_EXIT_CODE = 124
@@ -1041,6 +1042,41 @@ def render_functional_requirement( # noqa: C901
10411042
run_state.render_id,
10421043
)
10431044

1045+
# fixed_implementation_code_diff = git_utils.get_fixed_implementation_code_diff(args.build_folder, frid)
1046+
# if fixed_implementation_code_diff is None:
1047+
# raise Exception(
1048+
# "Fixes to the implementation code found during conformance testing are not committed to git."
1049+
# )
1050+
1051+
# git_utils.checkout_commit_with_frid(args.build_folder, previous_frid)
1052+
# existing_files = file_utils.list_all_text_files(args.build_folder)
1053+
# existing_files_content = file_utils.get_existing_files_content(args.build_folder, existing_files)
1054+
# git_utils.checkout_previous_branch(args.build_folder)
1055+
1056+
# implementation_code_diff = git_utils.get_implementation_code_diff(args.build_folder, frid, previous_frid)
1057+
1058+
# rendering_analysis = codeplainAPI.analyze_rendering(
1059+
# frid,
1060+
# plain_source_tree,
1061+
# linked_resources,
1062+
# existing_files_content,
1063+
# implementation_code_diff,
1064+
# fixed_implementation_code_diff,
1065+
# run_state,
1066+
# )
1067+
1068+
# if rendering_analysis:
1069+
# # TODO: Before this output is exposed to the user, we should check the 'guidance' field using LLM in the same way as we do conflicting requirements.
1070+
# console.info(
1071+
# f"Specification ambiguity detected! {AMBIGUITY_CAUSES[rendering_analysis['cause']]} of the functional requirement {frid}."
1072+
# )
1073+
# console.info(rendering_analysis["guidance"])
1074+
# run_state.add_rendering_analysis_for_frid(frid, rendering_analysis)
1075+
# else:
1076+
# console.warning(
1077+
# f"During conformance testing, the implementation code of the functional requirement {frid} was fixed, but the cause of the issue was not identified."
1078+
# )
1079+
10441080
conformance_tests_utils.dump_conformance_tests_json(conformance_tests)
10451081
git_utils.add_all_files_and_commit(
10461082
args.conformance_tests_folder,
@@ -1055,6 +1091,15 @@ def render_functional_requirement( # noqa: C901
10551091
None,
10561092
run_state.render_id,
10571093
)
1094+
1095+
# Copy build and conformance tests folders to output folders if specified
1096+
if args.copy_build:
1097+
file_utils.copy_folder_to_output(args.build_folder, args.build_dest)
1098+
if args.copy_conformance_tests:
1099+
file_utils.copy_folder_to_output(args.conformance_tests_folder, args.conformance_tests_dest)
1100+
1101+
codeplainAPI.finish_functional_requirement(frid, run_state)
1102+
10581103
return
10591104

10601105

@@ -1069,6 +1114,7 @@ def render(args, run_state: RunState): # noqa: C901
10691114
logging.getLogger("langsmith").setLevel(logging.WARNING)
10701115
logging.getLogger("git").setLevel(logging.WARNING)
10711116
logging.getLogger("anthropic._base_client").setLevel(logging.DEBUG)
1117+
logging.getLogger("services.langsmith.langsmith_service").setLevel(logging.INFO)
10721118

10731119
# Try to load logging configuration from YAML file
10741120
if os.path.exists(LOGGING_CONFIG_PATH):
@@ -1123,7 +1169,7 @@ def render(args, run_state: RunState): # noqa: C901
11231169

11241170
console.info(f"Rendering {args.filename} to target code.")
11251171

1126-
plain_source_tree = codeplainAPI.get_plain_source_tree(plain_source, loaded_templates)
1172+
plain_source_tree = codeplainAPI.get_plain_source_tree(plain_source, loaded_templates, run_state)
11271173

11281174
if args.render_range is not None:
11291175
args.render_range = get_render_range(args.render_range, plain_source_tree)
@@ -1146,6 +1192,17 @@ def render(args, run_state: RunState): # noqa: C901
11461192
)
11471193
frid = plain_spec.get_next_frid(plain_source_tree, frid)
11481194

1195+
# if args.verbose and len(run_state.frid_render_anaysis.items()) > 0:
1196+
# console.warning("\nWhile generating the code following ambiguities in the specs were identified:\n")
1197+
# for frid, render_analysis in run_state.frid_render_anaysis.items():
1198+
# specifications, _ = plain_spec.get_specifications_for_frid(plain_source_tree, frid)
1199+
# functional_requirement_text = specifications[plain_spec.FUNCTIONAL_REQUIREMENTS][-1]
1200+
# console.info("\n-------------------------------------")
1201+
# console.info(f"Functional requirement {frid}:")
1202+
# console.output(f"{functional_requirement_text}")
1203+
# console.info(f"Guidance: {render_analysis['guidance']}")
1204+
# console.info("-------------------------------------\n")
1205+
11491206
# Copy build and conformance tests folders to output folders if specified
11501207
if args.copy_build:
11511208
file_utils.copy_folder_to_output(args.build_folder, args.build_dest)
@@ -1225,10 +1282,6 @@ def exit_with_error(message, last_successful_frid=None, render_id=None):
12251282

12261283
codeplain_api = importlib.import_module(codeplain_api_module_name)
12271284

1228-
if not args.api_key or args.api_key == "":
1229-
exit_with_error(
1230-
"Error: API key is not provided. Please provide an API key using the --api-key flag or by setting the CLAUDE_API_KEY environment variable."
1231-
)
12321285
run_state = RunState(replay_with=args.replay_with)
12331286
console.debug(f"Render ID: {run_state.render_id}")
12341287

plain2code_arguments.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
from plain2code_read_config import get_args_from_config
66

7-
CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY")
7+
CODEPLAIN_API_KEY = os.getenv("CODEPLAIN_API_KEY")
8+
if not CODEPLAIN_API_KEY:
9+
CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY")
10+
if not CLAUDE_API_KEY:
11+
raise ValueError("CODEPLAIN_API_KEY or CLAUDE_API_KEY environment variable is not set")
12+
13+
814
DEFAULT_BUILD_FOLDER = "build"
915
DEFAULT_CONFORMANCE_TESTS_FOLDER = "conformance_tests"
1016
DEFAULT_BUILD_DEST = "dist"
@@ -179,8 +185,8 @@ def create_parser():
179185
parser.add_argument(
180186
"--api-key",
181187
type=str,
182-
default=CLAUDE_API_KEY,
183-
help="API key used to access the API. If not provided, the CLAUDE_API_KEY environment variable is used.",
188+
default=CODEPLAIN_API_KEY,
189+
help="API key used to access the API. If not provided, the CODEPLAIN_API_KEY environment variable is used.",
184190
)
185191
parser.add_argument(
186192
"--full-plain",

plain2code_nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def load(
9292
source, full_name, uptodate, matter = self.get_source(env, name, context=context, **kwargs)
9393
whitespaces = kwargs.get("whitespaces", 0)
9494
assert isinstance(whitespaces, int)
95-
source = source.replace("\n", "\n" + " " * whitespaces)
95+
source = source.rstrip().replace("\n", "\n" + " " * whitespaces)
9696

9797
path = Path(full_name)
9898

0 commit comments

Comments
 (0)