gptdiff 0.1.8__tar.gz → 0.1.9__tar.gz
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-0.1.8 → gptdiff-0.1.9}/PKG-INFO +1 -1
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff/gptdiff.py +50 -8
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/PKG-INFO +1 -1
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/SOURCES.txt +1 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/setup.py +1 -1
- gptdiff-0.1.9/tests/test_diff_parse.py +35 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/tests/test_smartapply.py +1 -1
- {gptdiff-0.1.8 → gptdiff-0.1.9}/LICENSE.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/README.md +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff/__init__.py +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/dependency_links.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/entry_points.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/requires.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/gptdiff.egg-info/top_level.txt +0 -0
- {gptdiff-0.1.8 → gptdiff-0.1.9}/setup.cfg +0 -0
@@ -67,6 +67,25 @@ def create_think_toolbox():
|
|
67
67
|
)
|
68
68
|
return toolbox
|
69
69
|
|
70
|
+
def color_code_diff(diff_text: str) -> str:
|
71
|
+
"""
|
72
|
+
Color code lines in a diff. Lines beginning with '-' in red, and
|
73
|
+
lines beginning with '+' in green.
|
74
|
+
"""
|
75
|
+
red = "\033[31m"
|
76
|
+
green = "\033[32m"
|
77
|
+
reset = "\033[0m"
|
78
|
+
|
79
|
+
colorized_lines = []
|
80
|
+
for line in diff_text.split('\n'):
|
81
|
+
if line.startswith('-'):
|
82
|
+
colorized_lines.append(f"{red}{line}{reset}")
|
83
|
+
elif line.startswith('+'):
|
84
|
+
colorized_lines.append(f"{green}{line}{reset}")
|
85
|
+
else:
|
86
|
+
colorized_lines.append(line)
|
87
|
+
|
88
|
+
return '\n'.join(colorized_lines)
|
70
89
|
|
71
90
|
def load_gitignore_patterns(gitignore_path):
|
72
91
|
with open(gitignore_path, 'r') as f:
|
@@ -308,8 +327,16 @@ def smartapply(diff_text, files, model=None, api_key=None, base_url=None):
|
|
308
327
|
updated = call_llm_for_apply_with_think_tool_available(path, original, patch, model, api_key=api_key, base_url=base_url)
|
309
328
|
files[path] = updated.strip()
|
310
329
|
|
330
|
+
threads = []
|
331
|
+
|
311
332
|
for path, patch in parsed_diffs:
|
312
|
-
process_file(path, patch)
|
333
|
+
thread = threading.Thread(target=process_file, args=(path, patch))
|
334
|
+
thread.start()
|
335
|
+
threads.append(thread)
|
336
|
+
|
337
|
+
# Wait for all threads to complete
|
338
|
+
for thread in threads:
|
339
|
+
thread.join()
|
313
340
|
|
314
341
|
return files
|
315
342
|
|
@@ -339,6 +366,7 @@ def parse_arguments():
|
|
339
366
|
parser.add_argument('--temperature', type=float, default=0.7, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
340
367
|
parser.add_argument('--max_tokens', type=int, default=30000, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
341
368
|
parser.add_argument('--model', type=str, default=None, help='Model to use for the API call.')
|
369
|
+
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
370
|
|
343
371
|
parser.add_argument('--nowarn', action='store_true', help='Disable large token warning')
|
344
372
|
|
@@ -404,11 +432,11 @@ def parse_diff_per_file(diff_text):
|
|
404
432
|
|
405
433
|
return diffs
|
406
434
|
|
407
|
-
def call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, model, api_key=None, base_url=None):
|
435
|
+
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
436
|
parser = FlatXMLParser("think")
|
409
437
|
formatter = FlatXMLPromptFormatter(tag="think")
|
410
438
|
toolbox = create_think_toolbox()
|
411
|
-
full_response = call_llm_for_apply(file_path, original_content, file_diff, model, api_key=
|
439
|
+
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
440
|
notool_response = ""
|
413
441
|
events = parser.parse(full_response)
|
414
442
|
is_in_tool = False
|
@@ -424,7 +452,7 @@ def call_llm_for_apply_with_think_tool_available(file_path, original_content, fi
|
|
424
452
|
|
425
453
|
return notool_response
|
426
454
|
|
427
|
-
def call_llm_for_apply(file_path, original_content, file_diff, model, api_key=None, base_url=None):
|
455
|
+
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
456
|
"""AI-powered diff application with conflict resolution.
|
429
457
|
|
430
458
|
Internal workhorse for smartapply that handles individual file patches.
|
@@ -474,6 +502,8 @@ Diff to apply:
|
|
474
502
|
{file_diff}
|
475
503
|
</diff>"""
|
476
504
|
|
505
|
+
if extra_prompt:
|
506
|
+
user_prompt += f"\n\n{extra_prompt}"
|
477
507
|
if model == "gemini-2.0-flash-thinking-exp-01-21":
|
478
508
|
user_prompt = system_prompt+"\n"+user_prompt
|
479
509
|
messages = [
|
@@ -490,7 +520,7 @@ Diff to apply:
|
|
490
520
|
response = client.chat.completions.create(model=model,
|
491
521
|
messages=messages,
|
492
522
|
temperature=0.0,
|
493
|
-
max_tokens=
|
523
|
+
max_tokens=max_tokens)
|
494
524
|
full_response = response.choices[0].message.content
|
495
525
|
|
496
526
|
elapsed = time.time() - start_time
|
@@ -590,12 +620,21 @@ def main():
|
|
590
620
|
if confirmation != 'y':
|
591
621
|
print("Request canceled")
|
592
622
|
sys.exit(0)
|
593
|
-
|
623
|
+
try:
|
624
|
+
full_text, diff_text, prompt_tokens, completion_tokens, total_tokens, cost = call_llm_for_diff(system_prompt, user_prompt, files_content, args.model,
|
594
625
|
temperature=args.temperature,
|
595
626
|
api_key=os.getenv('GPTDIFF_LLM_API_KEY'),
|
596
627
|
base_url=os.getenv('GPTDIFF_LLM_BASE_URL', "https://nano-gpt.com/api/v1/"),
|
597
628
|
max_tokens=args.max_tokens
|
598
629
|
)
|
630
|
+
except Exception as e:
|
631
|
+
full_text = f"{e}"
|
632
|
+
diff_text = ""
|
633
|
+
prompt_tokens = 0
|
634
|
+
completion_tokens = 0
|
635
|
+
total_tokens = 0
|
636
|
+
cost = 0
|
637
|
+
print(f"Error in LLM response {e}")
|
599
638
|
|
600
639
|
if(diff_text.strip() == ""):
|
601
640
|
print(f"\033[1;33mThere was no data in this diff. The LLM may have returned something invalid.\033[0m")
|
@@ -608,7 +647,7 @@ def main():
|
|
608
647
|
elif args.apply:
|
609
648
|
print("\nAttempting apply with the following diff:")
|
610
649
|
print("\n<diff>")
|
611
|
-
print(diff_text)
|
650
|
+
print(color_code_diff(diff_text))
|
612
651
|
print("\n</diff>")
|
613
652
|
print("Saved to patch.diff")
|
614
653
|
if apply_diff(project_dir, diff_text):
|
@@ -651,8 +690,11 @@ def main():
|
|
651
690
|
print("SMARTAPPLY")
|
652
691
|
print(file_diff)
|
653
692
|
print("-" * 40)
|
693
|
+
if args.applymodel is None:
|
694
|
+
args.applymodel = args.model
|
695
|
+
|
654
696
|
try:
|
655
|
-
updated_content = call_llm_for_apply_with_think_tool_available(file_path, original_content, file_diff, args.
|
697
|
+
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
698
|
|
657
699
|
if updated_content.strip() == "":
|
658
700
|
print("Cowardly refusing to write empty file to", file_path, "merge failed")
|
@@ -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.9',
|
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
|
File without changes
|