gptdiff 0.1.8__tar.gz → 0.1.10__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {gptdiff-0.1.8 → gptdiff-0.1.10}/PKG-INFO +39 -6
- {gptdiff-0.1.8 → gptdiff-0.1.10}/README.md +38 -5
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff/gptdiff.py +53 -8
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/PKG-INFO +39 -6
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/SOURCES.txt +1 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/setup.py +1 -1
- gptdiff-0.1.10/tests/test_diff_parse.py +35 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/tests/test_smartapply.py +1 -1
- {gptdiff-0.1.8 → gptdiff-0.1.10}/LICENSE.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff/__init__.py +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/dependency_links.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/entry_points.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/requires.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/gptdiff.egg-info/top_level.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: gptdiff
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.10
|
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
|
@@ -27,21 +27,54 @@ Dynamic: summary
|
|
27
27
|
|
28
28
|
# GPTDiff
|
29
29
|
|
30
|
-
🚀 **
|
30
|
+
🚀 **Create and apply diffs with AI** - Modify projects with natural language
|
31
|
+
|
32
|
+
More docs at [gptdiff.255labs.xyz](gptdiff.255labs.xyz)
|
33
|
+
|
34
|
+
### Example Usage of `gptdiff`
|
35
|
+
|
36
|
+
#### Apply a Patch Directly
|
37
|
+
```
|
38
|
+
bash
|
39
|
+
gptdiff "Add button animations on press" --apply
|
40
|
+
```
|
41
|
+
✅ Successfully applied patch
|
42
|
+
|
43
|
+
#### Generate a Patch File
|
44
|
+
```
|
45
|
+
bash
|
46
|
+
gptdiff "Add API documentation" --call
|
47
|
+
```
|
48
|
+
🔧 Patch written to `diff.patch`
|
49
|
+
|
50
|
+
#### Generate a Prompt File Without Calling LLM
|
51
|
+
```
|
52
|
+
bash
|
53
|
+
gptdiff "Improve error messages"
|
54
|
+
```
|
55
|
+
📄 LLM not called, written to `prompt.txt`
|
56
|
+
|
57
|
+
---
|
58
|
+
|
59
|
+
### Basic Usage
|
31
60
|
|
32
61
|
```bash
|
33
62
|
cd myproject
|
34
63
|
gptdiff 'add hover effects to the buttons'
|
35
64
|
```
|
36
65
|
|
37
|
-
Generates a prompt.txt file that you can copy and paste into
|
66
|
+
Generates a prompt.txt file that you can copy and paste into an LLM
|
38
67
|
|
39
|
-
|
68
|
+
### Simple command line agent loops
|
40
69
|
|
41
70
|
```bash
|
42
|
-
|
71
|
+
while
|
72
|
+
do
|
73
|
+
gptdiff "Add missing test cases" --apply
|
74
|
+
done
|
43
75
|
```
|
44
|
-
|
76
|
+
|
77
|
+
*Requires reasoning model*
|
45
78
|
|
46
79
|
### Why GPTDiff?
|
47
80
|
|
@@ -1,20 +1,53 @@
|
|
1
1
|
# GPTDiff
|
2
2
|
|
3
|
-
🚀 **
|
3
|
+
🚀 **Create and apply diffs with AI** - Modify projects with natural language
|
4
|
+
|
5
|
+
More docs at [gptdiff.255labs.xyz](gptdiff.255labs.xyz)
|
6
|
+
|
7
|
+
### Example Usage of `gptdiff`
|
8
|
+
|
9
|
+
#### Apply a Patch Directly
|
10
|
+
```
|
11
|
+
bash
|
12
|
+
gptdiff "Add button animations on press" --apply
|
13
|
+
```
|
14
|
+
✅ Successfully applied patch
|
15
|
+
|
16
|
+
#### Generate a Patch File
|
17
|
+
```
|
18
|
+
bash
|
19
|
+
gptdiff "Add API documentation" --call
|
20
|
+
```
|
21
|
+
🔧 Patch written to `diff.patch`
|
22
|
+
|
23
|
+
#### Generate a Prompt File Without Calling LLM
|
24
|
+
```
|
25
|
+
bash
|
26
|
+
gptdiff "Improve error messages"
|
27
|
+
```
|
28
|
+
📄 LLM not called, written to `prompt.txt`
|
29
|
+
|
30
|
+
---
|
31
|
+
|
32
|
+
### Basic Usage
|
4
33
|
|
5
34
|
```bash
|
6
35
|
cd myproject
|
7
36
|
gptdiff 'add hover effects to the buttons'
|
8
37
|
```
|
9
38
|
|
10
|
-
Generates a prompt.txt file that you can copy and paste into
|
39
|
+
Generates a prompt.txt file that you can copy and paste into an LLM
|
11
40
|
|
12
|
-
|
41
|
+
### Simple command line agent loops
|
13
42
|
|
14
43
|
```bash
|
15
|
-
|
44
|
+
while
|
45
|
+
do
|
46
|
+
gptdiff "Add missing test cases" --apply
|
47
|
+
done
|
16
48
|
```
|
17
|
-
|
49
|
+
|
50
|
+
*Requires reasoning model*
|
18
51
|
|
19
52
|
### Why GPTDiff?
|
20
53
|
|
@@ -44,6 +44,9 @@ a/file.py b/file.py
|
|
44
44
|
@@ -1,2 +1,2 @@
|
45
45
|
-def old():
|
46
46
|
+def new():
|
47
|
+
|
48
|
+
-
|
49
|
+
You must include the '--- file' and/or '+++ file' part of the diff. File modifications should include both.
|
47
50
|
"""
|
48
51
|
)
|
49
52
|
return toolbox
|
@@ -67,6 +70,25 @@ def create_think_toolbox():
|
|
67
70
|
)
|
68
71
|
return toolbox
|
69
72
|
|
73
|
+
def color_code_diff(diff_text: str) -> str:
|
74
|
+
"""
|
75
|
+
Color code lines in a diff. Lines beginning with '-' in red, and
|
76
|
+
lines beginning with '+' in green.
|
77
|
+
"""
|
78
|
+
red = "\033[31m"
|
79
|
+
green = "\033[32m"
|
80
|
+
reset = "\033[0m"
|
81
|
+
|
82
|
+
colorized_lines = []
|
83
|
+
for line in diff_text.split('\n'):
|
84
|
+
if line.startswith('-'):
|
85
|
+
colorized_lines.append(f"{red}{line}{reset}")
|
86
|
+
elif line.startswith('+'):
|
87
|
+
colorized_lines.append(f"{green}{line}{reset}")
|
88
|
+
else:
|
89
|
+
colorized_lines.append(line)
|
90
|
+
|
91
|
+
return '\n'.join(colorized_lines)
|
70
92
|
|
71
93
|
def load_gitignore_patterns(gitignore_path):
|
72
94
|
with open(gitignore_path, 'r') as f:
|
@@ -308,8 +330,16 @@ def smartapply(diff_text, files, model=None, api_key=None, base_url=None):
|
|
308
330
|
updated = call_llm_for_apply_with_think_tool_available(path, original, patch, model, api_key=api_key, base_url=base_url)
|
309
331
|
files[path] = updated.strip()
|
310
332
|
|
333
|
+
threads = []
|
334
|
+
|
311
335
|
for path, patch in parsed_diffs:
|
312
|
-
process_file(path, patch)
|
336
|
+
thread = threading.Thread(target=process_file, args=(path, patch))
|
337
|
+
thread.start()
|
338
|
+
threads.append(thread)
|
339
|
+
|
340
|
+
# Wait for all threads to complete
|
341
|
+
for thread in threads:
|
342
|
+
thread.join()
|
313
343
|
|
314
344
|
return files
|
315
345
|
|
@@ -339,6 +369,7 @@ def parse_arguments():
|
|
339
369
|
parser.add_argument('--temperature', type=float, default=0.7, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
340
370
|
parser.add_argument('--max_tokens', type=int, default=30000, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
341
371
|
parser.add_argument('--model', type=str, default=None, help='Model to use for the API call.')
|
372
|
+
parser.add_argument('--applymodel', type=str, default=None, help='Model to use for applying the diff. Defaults to the value of --model if not specified.')
|
342
373
|
|
343
374
|
parser.add_argument('--nowarn', action='store_true', help='Disable large token warning')
|
344
375
|
|
@@ -404,11 +435,11 @@ def parse_diff_per_file(diff_text):
|
|
404
435
|
|
405
436
|
return diffs
|
406
437
|
|
407
|
-
def call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, model, api_key=None, base_url=None):
|
438
|
+
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):
|
408
439
|
parser = FlatXMLParser("think")
|
409
440
|
formatter = FlatXMLPromptFormatter(tag="think")
|
410
441
|
toolbox = create_think_toolbox()
|
411
|
-
full_response = call_llm_for_apply(file_path, original_content, file_diff, model, api_key=
|
442
|
+
full_response = call_llm_for_apply(file_path, original_content, file_diff, model, api_key=api_key, base_url=base_url, extra_prompt=extra_prompt, max_tokens=max_tokens)
|
412
443
|
notool_response = ""
|
413
444
|
events = parser.parse(full_response)
|
414
445
|
is_in_tool = False
|
@@ -424,7 +455,7 @@ def call_llm_for_apply_with_think_tool_available(file_path, original_content, fi
|
|
424
455
|
|
425
456
|
return notool_response
|
426
457
|
|
427
|
-
def call_llm_for_apply(file_path, original_content, file_diff, model, api_key=None, base_url=None):
|
458
|
+
def call_llm_for_apply(file_path, original_content, file_diff, model, api_key=None, base_url=None, extra_prompt=None, max_tokens=30000):
|
428
459
|
"""AI-powered diff application with conflict resolution.
|
429
460
|
|
430
461
|
Internal workhorse for smartapply that handles individual file patches.
|
@@ -474,6 +505,8 @@ Diff to apply:
|
|
474
505
|
{file_diff}
|
475
506
|
</diff>"""
|
476
507
|
|
508
|
+
if extra_prompt:
|
509
|
+
user_prompt += f"\n\n{extra_prompt}"
|
477
510
|
if model == "gemini-2.0-flash-thinking-exp-01-21":
|
478
511
|
user_prompt = system_prompt+"\n"+user_prompt
|
479
512
|
messages = [
|
@@ -490,7 +523,7 @@ Diff to apply:
|
|
490
523
|
response = client.chat.completions.create(model=model,
|
491
524
|
messages=messages,
|
492
525
|
temperature=0.0,
|
493
|
-
max_tokens=
|
526
|
+
max_tokens=max_tokens)
|
494
527
|
full_response = response.choices[0].message.content
|
495
528
|
|
496
529
|
elapsed = time.time() - start_time
|
@@ -590,12 +623,21 @@ def main():
|
|
590
623
|
if confirmation != 'y':
|
591
624
|
print("Request canceled")
|
592
625
|
sys.exit(0)
|
593
|
-
|
626
|
+
try:
|
627
|
+
full_text, diff_text, prompt_tokens, completion_tokens, total_tokens, cost = call_llm_for_diff(system_prompt, user_prompt, files_content, args.model,
|
594
628
|
temperature=args.temperature,
|
595
629
|
api_key=os.getenv('GPTDIFF_LLM_API_KEY'),
|
596
630
|
base_url=os.getenv('GPTDIFF_LLM_BASE_URL', "https://nano-gpt.com/api/v1/"),
|
597
631
|
max_tokens=args.max_tokens
|
598
632
|
)
|
633
|
+
except Exception as e:
|
634
|
+
full_text = f"{e}"
|
635
|
+
diff_text = ""
|
636
|
+
prompt_tokens = 0
|
637
|
+
completion_tokens = 0
|
638
|
+
total_tokens = 0
|
639
|
+
cost = 0
|
640
|
+
print(f"Error in LLM response {e}")
|
599
641
|
|
600
642
|
if(diff_text.strip() == ""):
|
601
643
|
print(f"\033[1;33mThere was no data in this diff. The LLM may have returned something invalid.\033[0m")
|
@@ -608,7 +650,7 @@ def main():
|
|
608
650
|
elif args.apply:
|
609
651
|
print("\nAttempting apply with the following diff:")
|
610
652
|
print("\n<diff>")
|
611
|
-
print(diff_text)
|
653
|
+
print(color_code_diff(diff_text))
|
612
654
|
print("\n</diff>")
|
613
655
|
print("Saved to patch.diff")
|
614
656
|
if apply_diff(project_dir, diff_text):
|
@@ -651,8 +693,11 @@ def main():
|
|
651
693
|
print("SMARTAPPLY")
|
652
694
|
print(file_diff)
|
653
695
|
print("-" * 40)
|
696
|
+
if args.applymodel is None:
|
697
|
+
args.applymodel = args.model
|
698
|
+
|
654
699
|
try:
|
655
|
-
updated_content = call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, args.
|
700
|
+
updated_content = call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, args.applymodel, extra_prompt=f"This changeset is from the following instructions:\n{user_prompt}", max_tokens=args.max_tokens)
|
656
701
|
|
657
702
|
if updated_content.strip() == "":
|
658
703
|
print("Cowardly refusing to write empty file to", file_path, "merge failed")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: gptdiff
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.10
|
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
|
@@ -27,21 +27,54 @@ Dynamic: summary
|
|
27
27
|
|
28
28
|
# GPTDiff
|
29
29
|
|
30
|
-
🚀 **
|
30
|
+
🚀 **Create and apply diffs with AI** - Modify projects with natural language
|
31
|
+
|
32
|
+
More docs at [gptdiff.255labs.xyz](gptdiff.255labs.xyz)
|
33
|
+
|
34
|
+
### Example Usage of `gptdiff`
|
35
|
+
|
36
|
+
#### Apply a Patch Directly
|
37
|
+
```
|
38
|
+
bash
|
39
|
+
gptdiff "Add button animations on press" --apply
|
40
|
+
```
|
41
|
+
✅ Successfully applied patch
|
42
|
+
|
43
|
+
#### Generate a Patch File
|
44
|
+
```
|
45
|
+
bash
|
46
|
+
gptdiff "Add API documentation" --call
|
47
|
+
```
|
48
|
+
🔧 Patch written to `diff.patch`
|
49
|
+
|
50
|
+
#### Generate a Prompt File Without Calling LLM
|
51
|
+
```
|
52
|
+
bash
|
53
|
+
gptdiff "Improve error messages"
|
54
|
+
```
|
55
|
+
📄 LLM not called, written to `prompt.txt`
|
56
|
+
|
57
|
+
---
|
58
|
+
|
59
|
+
### Basic Usage
|
31
60
|
|
32
61
|
```bash
|
33
62
|
cd myproject
|
34
63
|
gptdiff 'add hover effects to the buttons'
|
35
64
|
```
|
36
65
|
|
37
|
-
Generates a prompt.txt file that you can copy and paste into
|
66
|
+
Generates a prompt.txt file that you can copy and paste into an LLM
|
38
67
|
|
39
|
-
|
68
|
+
### Simple command line agent loops
|
40
69
|
|
41
70
|
```bash
|
42
|
-
|
71
|
+
while
|
72
|
+
do
|
73
|
+
gptdiff "Add missing test cases" --apply
|
74
|
+
done
|
43
75
|
```
|
44
|
-
|
76
|
+
|
77
|
+
*Requires reasoning model*
|
45
78
|
|
46
79
|
### Why GPTDiff?
|
47
80
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
2
2
|
|
3
3
|
setup(
|
4
4
|
name='gptdiff',
|
5
|
-
version='0.1.
|
5
|
+
version='0.1.10',
|
6
6
|
description='A tool to generate and apply git diffs using LLMs',
|
7
7
|
author='255labs',
|
8
8
|
packages=find_packages(), # Use find_packages() to automatically discover packages
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import pytest
|
2
|
+
from gptdiff.gptdiff import color_code_diff
|
3
|
+
|
4
|
+
def test_color_code_diff():
|
5
|
+
"""
|
6
|
+
Test that lines starting with '-' are wrapped in red color codes
|
7
|
+
and lines starting with '+' are wrapped in green color codes.
|
8
|
+
"""
|
9
|
+
diff_text = """-this line is removed
|
10
|
+
+this line is added
|
11
|
+
unchanged line
|
12
|
+
-removed again
|
13
|
+
+and added again
|
14
|
+
some other neutral line"""
|
15
|
+
|
16
|
+
colorized = color_code_diff(diff_text)
|
17
|
+
|
18
|
+
# We expect lines beginning with '-' to be in red
|
19
|
+
assert "\033[31m-this line is removed\033[0m" in colorized
|
20
|
+
assert "\033[31m-removed again\033[0m" in colorized
|
21
|
+
|
22
|
+
# We expect lines beginning with '+' to be in green
|
23
|
+
assert "\033[32m+this line is added\033[0m" in colorized
|
24
|
+
assert "\033[32m+and added again\033[0m" in colorized
|
25
|
+
|
26
|
+
# Lines unchanged should remain uncolored
|
27
|
+
assert "unchanged line" in colorized
|
28
|
+
assert "some other neutral line" in colorized
|
29
|
+
|
30
|
+
# Ensure no erroneous color codes are added
|
31
|
+
# by counting them in the final output
|
32
|
+
color_code_count = colorized.count('\033[')
|
33
|
+
# We have four lines that should be color-coded, so we expect 4 * 2 = 8 color code inserts
|
34
|
+
# (start code + reset code per line)
|
35
|
+
assert color_code_count == 8
|
@@ -160,7 +160,7 @@ diff --git a/file2.py b/file2.py
|
|
160
160
|
}
|
161
161
|
|
162
162
|
# Mock LLM to return modified content based on file path
|
163
|
-
def mock_call_llm(file_path, original_content, file_diff, model, api_key, base_url):
|
163
|
+
def mock_call_llm(file_path, original_content, file_diff, model, api_key, base_url, extra_prompt=None, max_tokens=None):
|
164
164
|
if file_path == "file1.py":
|
165
165
|
return "def func1():\n print('New func1')"
|
166
166
|
elif file_path == "file2.py":
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|