gptdiff 0.1.10__py3-none-any.whl → 0.1.11__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.
- gptdiff/gptdiff.py +253 -53
- {gptdiff-0.1.10.dist-info → gptdiff-0.1.11.dist-info}/METADATA +27 -35
- gptdiff-0.1.11.dist-info/RECORD +8 -0
- gptdiff-0.1.10.dist-info/RECORD +0 -8
- {gptdiff-0.1.10.dist-info → gptdiff-0.1.11.dist-info}/LICENSE.txt +0 -0
- {gptdiff-0.1.10.dist-info → gptdiff-0.1.11.dist-info}/WHEEL +0 -0
- {gptdiff-0.1.10.dist-info → gptdiff-0.1.11.dist-info}/entry_points.txt +0 -0
- {gptdiff-0.1.10.dist-info → gptdiff-0.1.11.dist-info}/top_level.txt +0 -0
gptdiff/gptdiff.py
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
+
from pathlib import Path
|
3
|
+
import subprocess
|
4
|
+
import hashlib
|
5
|
+
import re
|
6
|
+
|
2
7
|
|
3
8
|
import openai
|
4
9
|
from openai import OpenAI
|
@@ -255,12 +260,20 @@ def build_environment(files_dict):
|
|
255
260
|
return '\n'.join(env)
|
256
261
|
|
257
262
|
def generate_diff(environment, goal, model=None, temperature=0.7, max_tokens=32000, api_key=None, base_url=None, prepend=None):
|
258
|
-
"""API: Generate diff from environment and goal
|
263
|
+
"""API: Generate a git diff from the environment and goal.
|
264
|
+
|
265
|
+
If 'prepend' is provided, it should be a path to a file whose content will be
|
266
|
+
prepended to the system prompt.
|
267
|
+
"""
|
259
268
|
if model is None:
|
260
269
|
model = os.getenv('GPTDIFF_MODEL', 'deepseek-reasoner')
|
261
270
|
if prepend:
|
262
|
-
prepend
|
263
|
-
|
271
|
+
if prepend.startswith("http://") or prepend.startswith("https://"):
|
272
|
+
import urllib.request
|
273
|
+
with urllib.request.urlopen(prepend) as response:
|
274
|
+
prepend = response.read().decode('utf-8')
|
275
|
+
else:
|
276
|
+
prepend = load_prepend_file(prepend)
|
264
277
|
else:
|
265
278
|
prepend = ""
|
266
279
|
|
@@ -343,18 +356,172 @@ def smartapply(diff_text, files, model=None, api_key=None, base_url=None):
|
|
343
356
|
|
344
357
|
return files
|
345
358
|
|
346
|
-
# Function to apply diff to project files
|
347
359
|
def apply_diff(project_dir, diff_text):
|
348
|
-
|
349
|
-
|
350
|
-
|
360
|
+
"""
|
361
|
+
Applies a unified diff (as generated by git diff) to the files in project_dir
|
362
|
+
using pure Python (without calling the external 'patch' command).
|
351
363
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
364
|
+
Handles file modifications, new file creation, and file deletions.
|
365
|
+
|
366
|
+
Returns:
|
367
|
+
True if at least one file was modified (or deleted/created) as a result of the patch,
|
368
|
+
False otherwise.
|
369
|
+
"""
|
370
|
+
from pathlib import Path
|
371
|
+
import re, hashlib
|
372
|
+
|
373
|
+
def file_hash(filepath):
|
374
|
+
h = hashlib.sha256()
|
375
|
+
with open(filepath, "rb") as f:
|
376
|
+
h.update(f.read())
|
377
|
+
return h.hexdigest()
|
378
|
+
|
379
|
+
def apply_patch_to_file(file_path, patch):
|
380
|
+
"""
|
381
|
+
Applies a unified diff patch (for a single file) to file_path.
|
382
|
+
|
383
|
+
Returns True if the patch was applied successfully, False otherwise.
|
384
|
+
"""
|
385
|
+
# Read the original file lines; if the file doesn't exist, treat it as empty.
|
386
|
+
if file_path.exists():
|
387
|
+
original_lines = file_path.read_text(encoding="utf8").splitlines(keepends=True)
|
388
|
+
else:
|
389
|
+
original_lines = []
|
390
|
+
new_lines = []
|
391
|
+
current_index = 0
|
392
|
+
|
393
|
+
patch_lines = patch.splitlines()
|
394
|
+
# Regex for a hunk header, e.g., @@ -3,7 +3,6 @@
|
395
|
+
hunk_header_re = re.compile(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@")
|
396
|
+
i = 0
|
397
|
+
while i < len(patch_lines):
|
398
|
+
line = patch_lines[i]
|
399
|
+
if line.startswith("@@"):
|
400
|
+
m = hunk_header_re.match(line)
|
401
|
+
if not m:
|
402
|
+
print("Invalid hunk header:", line)
|
403
|
+
return False
|
404
|
+
orig_start = int(m.group(1))
|
405
|
+
# orig_len = int(m.group(2)) if m.group(2) else 1 # not used explicitly
|
406
|
+
# new_start = int(m.group(3))
|
407
|
+
# new_len = int(m.group(4)) if m.group(4) else 1
|
408
|
+
|
409
|
+
# Copy unchanged lines before the hunk.
|
410
|
+
hunk_start_index = orig_start - 1 # diff headers are 1-indexed
|
411
|
+
if hunk_start_index > len(original_lines):
|
412
|
+
print("Hunk start index beyond file length")
|
413
|
+
return False
|
414
|
+
new_lines.extend(original_lines[current_index:hunk_start_index])
|
415
|
+
current_index = hunk_start_index
|
416
|
+
|
417
|
+
i += 1
|
418
|
+
# Process the hunk lines until the next hunk header.
|
419
|
+
while i < len(patch_lines) and not patch_lines[i].startswith("@@"):
|
420
|
+
pline = patch_lines[i]
|
421
|
+
if pline.startswith(" "):
|
422
|
+
# Context line must match exactly.
|
423
|
+
expected = pline[1:]
|
424
|
+
if current_index >= len(original_lines):
|
425
|
+
print("Context line expected but file ended")
|
426
|
+
return False
|
427
|
+
orig_line = original_lines[current_index].rstrip("\n")
|
428
|
+
if orig_line != expected:
|
429
|
+
print("Context line mismatch. Expected:", expected, "Got:", orig_line)
|
430
|
+
return False
|
431
|
+
new_lines.append(original_lines[current_index])
|
432
|
+
current_index += 1
|
433
|
+
elif pline.startswith("-"):
|
434
|
+
# Removal line: verify and skip from original.
|
435
|
+
expected = pline[1:]
|
436
|
+
if current_index >= len(original_lines):
|
437
|
+
print("Removal line expected but file ended")
|
438
|
+
return False
|
439
|
+
orig_line = original_lines[current_index].rstrip("\n")
|
440
|
+
if orig_line != expected:
|
441
|
+
print("Removal line mismatch. Expected:", expected, "Got:", orig_line)
|
442
|
+
return False
|
443
|
+
current_index += 1
|
444
|
+
elif pline.startswith("+"):
|
445
|
+
# Addition line: add to new_lines.
|
446
|
+
new_lines.append(pline[1:] + "\n")
|
447
|
+
else:
|
448
|
+
print("Unexpected line in hunk:", pline)
|
449
|
+
return False
|
450
|
+
i += 1
|
451
|
+
else:
|
452
|
+
# Skip non-hunk header lines.
|
453
|
+
i += 1
|
454
|
+
|
455
|
+
# Append any remaining lines from the original file.
|
456
|
+
new_lines.extend(original_lines[current_index:])
|
457
|
+
# Ensure parent directories exist before writing the file.
|
458
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
459
|
+
# Write the new content back to the file.
|
460
|
+
file_path.write_text("".join(new_lines), encoding="utf8")
|
356
461
|
return True
|
357
462
|
|
463
|
+
# Parse the diff into per-file patches.
|
464
|
+
file_patches = parse_diff_per_file(diff_text)
|
465
|
+
if not file_patches:
|
466
|
+
print("No file patches found in diff.")
|
467
|
+
return False
|
468
|
+
|
469
|
+
# Record original file hashes.
|
470
|
+
original_hashes = {}
|
471
|
+
for file_path, _ in file_patches:
|
472
|
+
target_file = Path(project_dir) / file_path
|
473
|
+
if target_file.exists():
|
474
|
+
original_hashes[file_path] = file_hash(target_file)
|
475
|
+
else:
|
476
|
+
original_hashes[file_path] = None
|
477
|
+
|
478
|
+
any_change = False
|
479
|
+
# Process each file patch.
|
480
|
+
for file_path, patch in file_patches:
|
481
|
+
target_file = Path(project_dir) / file_path
|
482
|
+
if "+++ /dev/null" in patch:
|
483
|
+
# Deletion patch: delete the file if it exists.
|
484
|
+
if target_file.exists():
|
485
|
+
target_file.unlink()
|
486
|
+
if not target_file.exists():
|
487
|
+
any_change = True
|
488
|
+
else:
|
489
|
+
print(f"Failed to delete file: {target_file}")
|
490
|
+
return False
|
491
|
+
else:
|
492
|
+
# Modification or new file creation.
|
493
|
+
success = apply_patch_to_file(target_file, patch)
|
494
|
+
if not success:
|
495
|
+
print(f"Failed to apply patch to file: {target_file}")
|
496
|
+
return False
|
497
|
+
|
498
|
+
# Verify that at least one file was changed by comparing hashes.
|
499
|
+
for file_path, patch in file_patches:
|
500
|
+
target_file = Path(project_dir) / file_path
|
501
|
+
if "+++ /dev/null" in patch:
|
502
|
+
if not target_file.exists():
|
503
|
+
any_change = True
|
504
|
+
else:
|
505
|
+
print(f"Expected deletion but file still exists: {target_file}")
|
506
|
+
return False
|
507
|
+
else:
|
508
|
+
old_hash = original_hashes.get(file_path)
|
509
|
+
if target_file.exists():
|
510
|
+
new_hash = file_hash(target_file)
|
511
|
+
if old_hash != new_hash:
|
512
|
+
any_change = True
|
513
|
+
else:
|
514
|
+
print(f"No change detected in file: {target_file}")
|
515
|
+
else:
|
516
|
+
print(f"Expected modification or creation but file is missing: {target_file}")
|
517
|
+
return False
|
518
|
+
|
519
|
+
if not any_change:
|
520
|
+
print("Patch applied but no file modifications detected.")
|
521
|
+
return False
|
522
|
+
return True
|
523
|
+
|
524
|
+
|
358
525
|
def parse_arguments():
|
359
526
|
parser = argparse.ArgumentParser(description='Generate and optionally apply git diffs using GPT-4.')
|
360
527
|
parser.add_argument('prompt', type=str, help='Prompt that runs on the codebase.')
|
@@ -399,41 +566,76 @@ def parse_diff_per_file(diff_text):
|
|
399
566
|
Note:
|
400
567
|
Uses 'b/' prefix detection from git diffs to determine target paths
|
401
568
|
"""
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
if
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
569
|
+
header_re = re.compile(r'^(?:diff --git\s+)?(a/[^ ]+)\s+(b/[^ ]+)\s*$', re.MULTILINE)
|
570
|
+
lines = diff_text.splitlines()
|
571
|
+
|
572
|
+
# Check if any header line exists.
|
573
|
+
if not any(header_re.match(line) for line in lines):
|
574
|
+
# Fallback strategy: detect file headers starting with '--- a/' or '-- a/'
|
575
|
+
diffs = []
|
576
|
+
current_lines = []
|
577
|
+
current_file = None
|
578
|
+
deletion_mode = False
|
579
|
+
header_line_re = re.compile(r'^-{2,3}\s+a/(.+)$')
|
580
|
+
|
581
|
+
for line in lines:
|
582
|
+
if header_line_re.match(line):
|
583
|
+
if current_file is not None and current_lines:
|
584
|
+
if deletion_mode and not any(l.startswith("+++ ") for l in current_lines):
|
585
|
+
current_lines.append("+++ /dev/null")
|
586
|
+
diffs.append((current_file, "\n".join(current_lines)))
|
587
|
+
current_lines = [line]
|
588
|
+
deletion_mode = False
|
589
|
+
file_from = header_line_re.match(line).group(1).strip()
|
590
|
+
current_file = file_from
|
591
|
+
else:
|
592
|
+
current_lines.append(line)
|
593
|
+
if "deleted file mode" in line:
|
594
|
+
deletion_mode = True
|
595
|
+
if line.startswith("+++ "):
|
596
|
+
parts = line.split()
|
597
|
+
if len(parts) >= 2:
|
598
|
+
file_to = parts[1].strip()
|
599
|
+
if file_to != "/dev/null":
|
600
|
+
current_file = file_to[2:] if (file_to.startswith("a/") or file_to.startswith("b/")) else file_to
|
601
|
+
if current_file is not None and current_lines:
|
602
|
+
if deletion_mode and not any(l.startswith("+++ ") for l in current_lines):
|
603
|
+
current_lines.append("+++ /dev/null")
|
604
|
+
diffs.append((current_file, "\n".join(current_lines)))
|
605
|
+
return diffs
|
606
|
+
else:
|
607
|
+
# Use header-based strategy.
|
608
|
+
diffs = []
|
609
|
+
current_lines = []
|
610
|
+
current_file = None
|
611
|
+
deletion_mode = False
|
612
|
+
for line in lines:
|
613
|
+
m = header_re.match(line)
|
614
|
+
if m:
|
615
|
+
if current_file is not None and current_lines:
|
616
|
+
if deletion_mode and not any(l.startswith("+++ ") for l in current_lines):
|
617
|
+
current_lines.append("+++ /dev/null")
|
618
|
+
diffs.append((current_file, "\n".join(current_lines)))
|
619
|
+
current_lines = [line]
|
620
|
+
deletion_mode = False
|
621
|
+
file_from = m.group(1) # e.g. "a/index.html"
|
622
|
+
file_to = m.group(2) # e.g. "b/index.html"
|
623
|
+
current_file = file_to[2:] if file_to.startswith("b/") else file_to
|
624
|
+
else:
|
625
|
+
current_lines.append(line)
|
626
|
+
if "deleted file mode" in line:
|
627
|
+
deletion_mode = True
|
628
|
+
if line.startswith("+++ "):
|
629
|
+
parts = line.split()
|
630
|
+
if len(parts) >= 2:
|
631
|
+
file_to = parts[1].strip()
|
632
|
+
if file_to != "/dev/null":
|
633
|
+
current_file = file_to[2:] if (file_to.startswith("a/") or file_to.startswith("b/")) else file_to
|
634
|
+
if current_file is not None and current_lines:
|
635
|
+
if deletion_mode and not any(l.startswith("+++ ") for l in current_lines):
|
636
|
+
current_lines.append("+++ /dev/null")
|
637
|
+
diffs.append((current_file, "\n".join(current_lines)))
|
638
|
+
return diffs
|
437
639
|
|
438
640
|
def call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, model, api_key=None, base_url=None, extra_prompt=None, max_tokens=30000):
|
439
641
|
parser = FlatXMLParser("think")
|
@@ -486,14 +688,12 @@ def call_llm_for_apply(file_path, original_content, file_diff, model, api_key=No
|
|
486
688
|
... )
|
487
689
|
>>> print(updated)
|
488
690
|
def new(): pass"""
|
489
|
-
|
490
691
|
system_prompt = """Please apply the diff to this file. Return the result in a block. Write the entire file.
|
491
692
|
|
492
693
|
1. Carefully apply all changes from the diff
|
493
694
|
2. Preserve surrounding context that isn't changed
|
494
695
|
3. Only return the final file content, do not add any additional markup and do not add a code block
|
495
696
|
4. You must return the entire file. It overwrites the existing file."""
|
496
|
-
|
497
697
|
user_prompt = f"""File: {file_path}
|
498
698
|
File contents:
|
499
699
|
<filecontents>
|
@@ -504,7 +704,6 @@ Diff to apply:
|
|
504
704
|
<diff>
|
505
705
|
{file_diff}
|
506
706
|
</diff>"""
|
507
|
-
|
508
707
|
if extra_prompt:
|
509
708
|
user_prompt += f"\n\n{extra_prompt}"
|
510
709
|
if model == "gemini-2.0-flash-thinking-exp-01-21":
|
@@ -513,7 +712,6 @@ Diff to apply:
|
|
513
712
|
{"role": "system", "content": system_prompt},
|
514
713
|
{"role": "user", "content": user_prompt},
|
515
714
|
]
|
516
|
-
|
517
715
|
if api_key is None:
|
518
716
|
api_key = os.getenv('GPTDIFF_LLM_API_KEY')
|
519
717
|
if base_url is None:
|
@@ -525,7 +723,6 @@ Diff to apply:
|
|
525
723
|
temperature=0.0,
|
526
724
|
max_tokens=max_tokens)
|
527
725
|
full_response = response.choices[0].message.content
|
528
|
-
|
529
726
|
elapsed = time.time() - start_time
|
530
727
|
minutes, seconds = divmod(int(elapsed), 60)
|
531
728
|
time_str = f"{minutes}m {seconds}s" if minutes else f"{seconds}s"
|
@@ -602,8 +799,9 @@ def main():
|
|
602
799
|
args.model = os.getenv('GPTDIFF_MODEL', 'deepseek-reasoner')
|
603
800
|
|
604
801
|
if not args.call and not args.apply:
|
802
|
+
append = "\nInstead of using <diff> tags, use ```diff backticks."
|
605
803
|
with open('prompt.txt', 'w') as f:
|
606
|
-
f.write(full_prompt)
|
804
|
+
f.write(full_prompt+append)
|
607
805
|
print(f"Total tokens: {token_count:5d}")
|
608
806
|
print(f"\033[1;32mNot calling GPT-4.\033[0m") # Green color for success message
|
609
807
|
print('Instead, wrote full prompt to prompt.txt. Use `xclip -selection clipboard < prompt.txt` then paste into chatgpt')
|
@@ -646,7 +844,6 @@ def main():
|
|
646
844
|
print("\a") # Terminal bell for completion notification
|
647
845
|
return
|
648
846
|
|
649
|
-
# Output result
|
650
847
|
elif args.apply:
|
651
848
|
print("\nAttempting apply with the following diff:")
|
652
849
|
print("\n<diff>")
|
@@ -728,3 +925,6 @@ def main():
|
|
728
925
|
print(f"Completion tokens: {completion_tokens}")
|
729
926
|
print(f"Total tokens: {total_tokens}")
|
730
927
|
#print(f"Total cost: ${cost:.4f}")
|
928
|
+
|
929
|
+
if __name__ == "__main__":
|
930
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: gptdiff
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.11
|
4
4
|
Summary: A tool to generate and apply git diffs using LLMs
|
5
5
|
Author: 255labs
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -26,10 +26,15 @@ Dynamic: requires-dist
|
|
26
26
|
Dynamic: summary
|
27
27
|
|
28
28
|
# GPTDiff
|
29
|
+
<!--
|
30
|
+
GPTDiff: Create and apply diffs using AI.
|
31
|
+
This tool leverages natural language instructions to modify project codebases.
|
32
|
+
-->
|
29
33
|
|
30
|
-
🚀 **Create and apply diffs with AI**
|
34
|
+
🚀 **Create and apply diffs with AI**
|
35
|
+
Modify your project using plain English.
|
31
36
|
|
32
|
-
More
|
37
|
+
More documentation at [gptdiff.255labs.xyz](gptdiff.255labs.xyz)
|
33
38
|
|
34
39
|
### Example Usage of `gptdiff`
|
35
40
|
|
@@ -63,7 +68,8 @@ cd myproject
|
|
63
68
|
gptdiff 'add hover effects to the buttons'
|
64
69
|
```
|
65
70
|
|
66
|
-
Generates a prompt.txt file
|
71
|
+
Generates a prompt.txt file containing the full request.
|
72
|
+
Copy and paste its content into your preferred LLM (e.g., ChatGPT) for further experimentation.
|
67
73
|
|
68
74
|
### Simple command line agent loops
|
69
75
|
|
@@ -147,7 +153,7 @@ First sign up for an API key at https://nano-gpt.com/api and generate your key.
|
|
147
153
|
export GPTDIFF_LLM_API_KEY='your-api-key'
|
148
154
|
# Optional: For switching API providers
|
149
155
|
export GPTDIFF_MODEL='deepseek-reasoner' # Set default model for all commands
|
150
|
-
export GPTDIFF_LLM_BASE_URL='https://nano-gpt.com/api/v1/
|
156
|
+
export GPTDIFF_LLM_BASE_URL='https://nano-gpt.com/api/v1/'
|
151
157
|
```
|
152
158
|
|
153
159
|
#### Windows
|
@@ -171,7 +177,7 @@ Prevent files being appended to the prompt by adding them to `.gitignore` or `.g
|
|
171
177
|
|
172
178
|
### Command Line Usage
|
173
179
|
|
174
|
-
After installing the package,
|
180
|
+
After installing the package, use the `gptdiff` command in your terminal. Change directory into your codebase and run:
|
175
181
|
|
176
182
|
```bash
|
177
183
|
gptdiff '<user_prompt>'
|
@@ -181,13 +187,7 @@ any files that are included in .gitignore are ignored when generating prompt.txt
|
|
181
187
|
|
182
188
|
#### Specifying Additional Files
|
183
189
|
|
184
|
-
You
|
185
|
-
|
186
|
-
Example usage:
|
187
|
-
|
188
|
-
```bash
|
189
|
-
gptdiff 'make this change' src test
|
190
|
-
```
|
190
|
+
You may supply extra files or directories as arguments to the `gptdiff` command. If omitted, the tool defaults to the current working directory, excluding those matching ignore rules.
|
191
191
|
|
192
192
|
#### Autopatch Changes
|
193
193
|
|
@@ -202,24 +202,20 @@ Preview changes without applying them by omitting the `--apply` flag when using
|
|
202
202
|
```bash
|
203
203
|
gptdiff "Modernize database queries" --call
|
204
204
|
```
|
205
|
-
<span style="color: #0066cc;"
|
205
|
+
<span style="color: #0066cc;">i️ Diff preview generated - review changes before applying</span>
|
206
206
|
|
207
207
|
This often generates incorrect diffs that need to be manually merged.
|
208
208
|
|
209
209
|
#### Smart Apply
|
210
210
|
|
211
|
-
For
|
211
|
+
For robust handling of complex changes, use `smartapply`. It processes each file’s diff individually via the LLM, ensuring nuanced conflict resolution.
|
212
212
|
|
213
|
-
|
214
|
-
gptdiff 'refactor authentication system' --apply
|
215
|
-
```
|
216
|
-
|
217
|
-
### Completion Notification
|
213
|
+
## Completion Notification
|
218
214
|
|
219
215
|
Use the `--nobeep` option to disable the default completion beep:
|
220
216
|
|
221
217
|
```bash
|
222
|
-
gptdiff '<user_prompt>' --
|
218
|
+
gptdiff '<user_prompt>' --nobeep
|
223
219
|
```
|
224
220
|
|
225
221
|
## Local API Documentation
|
@@ -242,29 +238,25 @@ import os
|
|
242
238
|
|
243
239
|
os.environ['GPTDIFF_LLM_API_KEY'] = 'your-api-key'
|
244
240
|
|
245
|
-
# Create
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
241
|
+
# Create files dictionary
|
242
|
+
files = {"main.py": "def old_name():\n print('Need renaming')"}
|
243
|
+
|
244
|
+
# Generate transformation diff using an environment string built from the files dictionary
|
245
|
+
environment = ""
|
246
|
+
for path, content in files.items():
|
247
|
+
environment += f"File: {path}\nContent:\n{content}\n"
|
252
248
|
|
253
|
-
# Generate transformation diff
|
254
249
|
diff = generate_diff(
|
255
250
|
environment=environment,
|
256
251
|
goal='Rename function to new_name()',
|
257
252
|
model='deepseek-reasoner'
|
258
253
|
)
|
259
254
|
|
260
|
-
# Apply changes safely
|
261
|
-
|
262
|
-
diff_text=diff,
|
263
|
-
environment_str=environment
|
264
|
-
)
|
255
|
+
# Apply changes safely using the files dict
|
256
|
+
updated_files = smartapply(diff, files)
|
265
257
|
|
266
258
|
print("Transformed codebase:")
|
267
|
-
print(
|
259
|
+
print(updated_files["main.py"])
|
268
260
|
```
|
269
261
|
|
270
262
|
**Batch Processing Example:**
|
@@ -0,0 +1,8 @@
|
|
1
|
+
gptdiff/__init__.py,sha256=yGjgwv7tNvH1ZLPsQyoo1CxpTOl1iCAwwDBp-_17ksQ,89
|
2
|
+
gptdiff/gptdiff.py,sha256=HbnFkP1o5jQ-WIC99y-et_X6BeBsArtKqiFb2FG6X28,36694
|
3
|
+
gptdiff-0.1.11.dist-info/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
|
4
|
+
gptdiff-0.1.11.dist-info/METADATA,sha256=pCTb2Mu4w0Y6HSAY5A8F8jwrJrLfq_SQw80PyQvRIXA,7799
|
5
|
+
gptdiff-0.1.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
+
gptdiff-0.1.11.dist-info/entry_points.txt,sha256=0yvXYEVAZFI-p32kQ4-h3qKVWS0a86jsM9FAwF89t9w,49
|
7
|
+
gptdiff-0.1.11.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
|
8
|
+
gptdiff-0.1.11.dist-info/RECORD,,
|
gptdiff-0.1.10.dist-info/RECORD
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
gptdiff/__init__.py,sha256=yGjgwv7tNvH1ZLPsQyoo1CxpTOl1iCAwwDBp-_17ksQ,89
|
2
|
-
gptdiff/gptdiff.py,sha256=78_Y1ifKxCdC-e8TdKm3kKDklyV0K8S0fKyDdXhLNQs,27706
|
3
|
-
gptdiff-0.1.10.dist-info/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
|
4
|
-
gptdiff-0.1.10.dist-info/METADATA,sha256=jw4gVLU2Gk7Iuqy-NdkeDlF9loiLfd7825lZxXIbQEY,7602
|
5
|
-
gptdiff-0.1.10.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
-
gptdiff-0.1.10.dist-info/entry_points.txt,sha256=0yvXYEVAZFI-p32kQ4-h3qKVWS0a86jsM9FAwF89t9w,49
|
7
|
-
gptdiff-0.1.10.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
|
8
|
-
gptdiff-0.1.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|