├── .gitignore ├── requirements.txt ├── README.md └── app.py /.gitignore: -------------------------------------------------------------------------------- 1 | flask_session/ -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=2.2 2 | werkzeug==2.2.2 3 | flask_session==0.8.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLM Code Helper Ver 2 2 | 3 | ## Description 4 | 5 | LLM Code Helper is a utility designed to facilitate interaction with a Language Learning Model (LLM) for suggesting code modifications. It provides a convenient way to format your code with line numbers, improving the LLM's understanding and accuracy. Additionally, this tool can apply the LLM's suggestions, which are returned in a JSON format, directly to your code. 6 | 7 | > :warning: **Important Note**: This application is currently in its testing phase. It may contain bugs, and there are likely areas for improvement to enhance usability. If you utilize this code, we kindly ask that you contribute back any improvements you make. Due to life's demands, we haven't had as much time as we'd like to refine this tool. Your contributions are greatly appreciated! 8 | 9 | ## Installation 10 | 11 | 1. Clone the repository to your local machine. 12 | 2. Navigate to the root directory of the project. 13 | 3. Run `pip install -r requirements.txt` to install all the necessary dependencies. 14 | 15 | ## Usage 16 | 17 | 1. Run `python app.py` to start the application. 18 | 2. Go to `http://localhost:5000` and paste your code into the box and press `Format Code`. 19 | 3. Copy the formatted code. 20 | 4. Provide your LLM instructions for what you want to do and then paste your copied code (with line numbers) after your instructions (make sure to put your pasted code in triple backticks ```` ``` ````). 21 | 5. Go to `http://localhost:5000` and navigate to `/process-changes`. 22 | 6. Paste your original code without line numbers into the first box. 23 | 7. Copy the JSON from the LLM response into the second box and press the `Process Changes` button. 24 | 8. Copy your new code with the copy button and paste it into your original codebase. 25 | 26 | ## Custom LLM Prompt 27 | 28 | Here is a LLM prompt I start with. Change the initial `Context:` instructions to suit your situation. 29 | 30 | ```` 31 | Context: I am working on existing code that I need to be modified. I will provide line numbers for each line of code. Use the line numbers as refences but ignore them otherwise. 32 | 33 | ``` 34 | Please provide your suggested code changes in the following JSON format: 35 | 36 | ```json 37 | [ 38 | { 39 | "type": "remove", 40 | "lines": "LINE_NUMBERS", 41 | "text": "", 42 | "first_original_line": "FIRST_LINE_OF_ORIGINAL_TEXT_TO_BE_REMOVED" 43 | }, 44 | { 45 | "type": "insertafter", 46 | "lines": "LINE_NUMBER", 47 | "text": "CODE_TO_INSERT", 48 | "first_original_line": "ORIGINAL_TEXT_AFTER_WHICH_CODE_SHOULD_BE_INSERTED" 49 | }, 50 | { 51 | "type": "replace", 52 | "lines": "LINE_NUMBERS", 53 | "text": "REPLACEMENT_CODE", 54 | "first_original_line": "FIRST_LINE_OF_ORIGINAL_TEXT_TO_BE_REPLACED" 55 | } 56 | ] 57 | ``` 58 | 59 | **Explanation of JSON Format:** 60 | 61 | * **Array of Changes:** The response should be a JSON array, where each element represents a single change to be made to the code. 62 | * **Change Object:** Each change object must have the following properties: 63 | * **type:** A string indicating the type of change. It can be one of the following: 64 | * `"remove"`: To delete lines of code. 65 | * `"insertafter"`: To insert code after a specific line. 66 | * `"replace"`: To replace lines of code with new code. 67 | * **lines:** A string specifying the line numbers affected by the change. 68 | * For `"remove"` and `"replace"`, this can be a single line number (e.g., `"15"`) or a range of lines (e.g., `"10-20"`). 69 | * For `"insertafter"`, this should be the line number after which the new code should be inserted. 70 | * **text:** A string containing the code to be inserted or used as a replacement. For `"remove"` changes, this should be an empty string (`""`). 71 | * **first_original_line:** Just the first line of the original code that is being modified. This is used to verify the original text before making changes. 72 | * For `"remove"`, this is the first line of the text being removed. 73 | * For `"insertafter"`, this is the line that will be above the inserted line. 74 | * For `"replace"`, this is the first line of the text being replaced. 75 | 76 | **Important Points to Prevent Off-by-One Errors:** 77 | 78 | 1. **Context Awareness:** Ensure that the `first_original_line` provided in the JSON matches exactly with the first line of the code to be removed, inserted after, or replaced. This line is crucial for accurately locating the position for changes. 79 | 2. **Line Number Calculation:** Verify that the line numbers specified in the `lines` field are correct and correspond to the actual lines in the original code. 80 | 3. **Review Before Applying:** Before applying the changes, review the JSON output to confirm that the line numbers and the `first_original_line` values match your expectations. This will help catch any off-by-one errors before they are introduced. 81 | 82 | **Example:** 83 | 84 | ```json 85 | [ 86 | { 87 | "type": "remove", 88 | "lines": "13", 89 | "text": "", 90 | "first_original_line": " echo 'Line to be removed';" 91 | }, 92 | { 93 | "type": "insertafter", 94 | "lines": "25", 95 | "text": " echo 'Inserted line of code';", 96 | "first_original_line": " echo 'Line above inserted code';" 97 | }, 98 | { 99 | "type": "replace", 100 | "lines": "30-32", 101 | "text": " function new_function() {\n return 'New function';\n }", 102 | "first_original_line": " function old_function() {" 103 | } 104 | ] 105 | ``` 106 | 107 | **Benefits:** 108 | 109 | * **Structured Data:** This JSON format provides a clear and structured way to represent code changes, making it easier to parse and apply them programmatically using the `process-changes` endpoint. 110 | * **Reduced Ambiguity:** The specific format reduces ambiguity and ensures that the AI assistant's instructions are interpreted correctly. 111 | * **Improved Efficiency:** By providing instructions in this format, you can streamline the process of applying code changes and avoid the need for manual interpretation or rewriting. 112 | * **Verification:** The inclusion of the original text allows for verification before making changes, reducing the risk of unintended modifications. 113 | 114 | If you understand my instructions and the context, please indicate it with a one-sentence response. 115 | ```` 116 | 117 | ## Contributing 118 | 119 | Contributions are welcome. Please open an issue or submit a pull request. 120 | 121 | ## License 122 | 123 | This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from flask import Flask, request, render_template_string, session, jsonify 4 | from flask_session import Session 5 | 6 | app = Flask(__name__) 7 | app.config['SECRET_KEY'] = 'your_secret_key_here' 8 | app.config['SESSION_TYPE'] = 'filesystem' 9 | Session(app) 10 | 11 | HTML = ''' 12 | 13 | 14 | 15 | 16 | 17 | LLM Code Helper 18 | 157 | 158 | 159 |

LLM Code Helper

160 | 161 |

Format Code

162 |
163 | 164 |
165 |
166 | 167 |
168 |
169 | 170 | 171 |
172 |
173 |
174 |
175 | 176 |

Process Changes

177 |
178 | 179 |
180 |
181 | 182 |
183 |
184 | 185 | 186 |
187 |
188 |
189 |
190 | 191 | 253 | 254 | 255 | ''' 256 | 257 | 258 | @app.route('/') 259 | def index(): 260 | return render_template_string(HTML) 261 | 262 | 263 | @app.route('/format-code', methods=['POST']) 264 | def format_code(): 265 | data = request.get_json() 266 | code = data['code'] 267 | lines = code.split('\n') 268 | # Ensure each line number is padded to the same width 269 | max_line_num_width = len(str(len(lines))) 270 | formatted_code = '\n'.join( 271 | f"{str(i + 1).rjust(max_line_num_width)}. {line}" for i, line in enumerate(lines)) 272 | session['last_code'] = code # Store the original code in the session 273 | return jsonify({'formatted_code': formatted_code, 'lines': len(lines)}) 274 | 275 | 276 | def sort_changes(changes): 277 | def change_key(change): 278 | start_line = int(change['lines'].split( 279 | '-')[0]) if '-' in change['lines'] else int(change['lines']) 280 | return start_line 281 | 282 | return sorted(changes, key=change_key) 283 | 284 | 285 | @app.route('/process-changes', methods=['POST']) 286 | def process_changes(): 287 | data = request.get_json() 288 | changes = json.loads(data['changes']) 289 | # Retrieve the last code from the session 290 | original_code = session.get('last_code', '') 291 | 292 | if not original_code: 293 | return jsonify({'error': 'No code to process. Please format code first.'}) 294 | 295 | lines = original_code.split('\n') 296 | 297 | # Sort changes from beginning to end of the document 298 | sorted_changes = sort_changes(changes) 299 | 300 | for change in reversed(sorted_changes): 301 | start, end = map(int, change['lines'].split( 302 | '-')) if '-' in change['lines'] else (int(change['lines']), int(change['lines'])) 303 | 304 | # Adjust for 0-based indexing 305 | start -= 1 306 | end -= 1 307 | 308 | # Verify only the first line of the original text before making changes 309 | first_original_line = lines[start].strip() 310 | if change['first_original_line'].strip() != first_original_line: 311 | # Print the lines around the mismatch for debugging 312 | context_lines = lines[max(0, start-2):min(len(lines), end+3)] 313 | context = '
'.join( 314 | [f"{i + max(0, start-2) + 1}: {line}" for i, line in enumerate(context_lines)]) 315 | error_message = ( 316 | f"Error: Original text mismatch at line { 317 | start + 1}.
" 318 | f"Expected: '{change['first_original_line'].strip() 319 | }'
" 320 | f"Found: '{first_original_line}'
" 321 | f"Context:
{context}" 322 | ) 323 | return jsonify({'error': error_message}) 324 | 325 | if change['type'] == 'remove': 326 | del lines[start:end + 1] 327 | elif change['type'] == 'insertafter': 328 | lines.insert(start + 1, change['text']) 329 | elif change['type'] == 'replace': 330 | lines[start:end + 1] = change['text'].split('\n') 331 | 332 | processed_code = '\n'.join(lines) 333 | # Update the stored code with the processed version 334 | session['last_code'] = processed_code 335 | 336 | # Format the processed code with line numbers for display purposes only 337 | max_line_num_width = len(str(len(lines))) 338 | formatted_processed_code = '\n'.join( 339 | f"{str(i + 1).rjust(max_line_num_width)}. {line}" for i, line in enumerate(lines)) 340 | 341 | return jsonify({'processed_code': processed_code, 'formatted_processed_code': formatted_processed_code, 'changes_made': len(sorted_changes)}) 342 | 343 | 344 | if __name__ == '__main__': 345 | app.run(debug=True) 346 | --------------------------------------------------------------------------------