gptdiff 0.1.13__tar.gz → 0.1.15__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gptdiff
3
- Version: 0.1.13
3
+ Version: 0.1.15
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
@@ -747,6 +747,69 @@ def build_environment_from_filelist(file_list, cwd):
747
747
  continue
748
748
  return build_environment(files_dict)
749
749
 
750
+ def smart_apply_patch(project_dir, diff_text, user_prompt, args):
751
+ """
752
+ Attempt to apply a diff via smartapply: process each file concurrently using the LLM.
753
+ """
754
+ from pathlib import Path
755
+ parsed_diffs = parse_diff_per_file(diff_text)
756
+ print("Found", len(parsed_diffs), "files in diff, processing smart apply concurrently:")
757
+ if len(parsed_diffs) == 0:
758
+ print("\033[1;33mThere were no entries in this diff. The LLM may have returned something invalid.\033[0m")
759
+ if args.beep:
760
+ print("\a")
761
+ return
762
+ threads = []
763
+
764
+ def process_file(file_path, file_diff):
765
+ full_path = Path(project_dir) / file_path
766
+ print(f"Processing file: {file_path}")
767
+ if '+++ /dev/null' in file_diff:
768
+ if full_path.exists():
769
+ full_path.unlink()
770
+ print(f"\033[1;32mDeleted file {file_path}.\033[0m")
771
+ else:
772
+ print(f"\033[1;33mFile {file_path} not found - skipping deletion\033[0m")
773
+ return
774
+ original_content = ''
775
+ if full_path.exists():
776
+ try:
777
+ original_content = full_path.read_text()
778
+ except UnicodeDecodeError:
779
+ print(f"Skipping binary file {file_path}")
780
+ return
781
+ if not hasattr(args, "applymodel") or args.applymodel is None:
782
+ args.applymodel = args.model
783
+ if args.applymodel is None:
784
+ args.applymodel = os.getenv("GPTDIFF_MODEL")
785
+
786
+ print("-" * 40)
787
+ print("Running smartapply with", args.applymodel,"on",file_path)
788
+ print("-" * 40)
789
+ try:
790
+ updated_content = call_llm_for_apply_with_think_tool_available(
791
+ file_path, original_content, file_diff, args.applymodel,
792
+ extra_prompt=f"This changeset is from the following instructions:\n{user_prompt}",
793
+ max_tokens=args.max_tokens)
794
+ if updated_content.strip() == "":
795
+ print("Cowardly refusing to write empty file to", file_path, "merge failed")
796
+ return
797
+ full_path.parent.mkdir(parents=True, exist_ok=True)
798
+ full_path.write_text(updated_content)
799
+ print(f"\033[1;32mSuccessful 'smartapply' update {file_path}.\033[0m")
800
+ except Exception as e:
801
+ print(f"\033[1;31mFailed to process {file_path}: {str(e)}\033[0m")
802
+
803
+ for file_path, file_diff in parsed_diffs:
804
+ thread = threading.Thread(target=process_file, args=(file_path, file_diff))
805
+ thread.start()
806
+ threads.append(thread)
807
+ for thread in threads:
808
+ thread.join()
809
+
810
+ if args.beep:
811
+ print("\a")
812
+
750
813
  def main():
751
814
  # Adding color support for Windows CMD
752
815
  if os.name == 'nt':
@@ -754,7 +817,6 @@ def main():
754
817
 
755
818
  args = parse_arguments()
756
819
 
757
- # TODO: The 'openai.api_base' option isn't read in the client API. You will need to pass it when you instantiate the client, e.g. 'OpenAI(base_url="https://nano-gpt.com/api/v1/")'
758
820
  # openai.api_base = "https://nano-gpt.com/api/v1/"
759
821
  if len(sys.argv) < 2:
760
822
  print("Usage: python script.py '<user_prompt>' [--apply]")
@@ -778,11 +840,24 @@ def main():
778
840
  project_files.extend(load_project_files(additional_path, project_dir))
779
841
 
780
842
  if args.prepend:
781
- prepend = load_prepend_file(args.prepend)
782
- print("Including prepend",len(enc.encode(json.dumps(prepend))), "tokens")
843
+ prepend = args.prepend
783
844
  else:
784
845
  prepend = ""
785
846
 
847
+ if prepend.startswith("http://") or prepend.startswith("https://"):
848
+ try:
849
+ import urllib.request
850
+ with urllib.request.urlopen(prepend) as response:
851
+ prepend = response.read().decode('utf-8')
852
+ except Exception as e:
853
+ print(f"Error fetching prepend content from URL {prepend}: {e}")
854
+ prepend = ""
855
+ elif os.path.exists(prepend):
856
+ prepend = load_prepend_file(prepend)
857
+ else:
858
+ # If the specified prepend path does not exist, treat the value as literal content.
859
+ prepend = prepend
860
+
786
861
  # Prepare system prompt
787
862
  system_prompt = prepend + f"Output a git diff into a <diff> block."
788
863
 
@@ -851,73 +926,11 @@ def main():
851
926
  print("\n</diff>")
852
927
  print("Saved to patch.diff")
853
928
  if apply_diff(project_dir, diff_text):
854
- print(f"\033[1;32mPatch applied successfully with 'git apply'.\033[0m") # Green color for success message
929
+ print(f"\033[1;32mPatch applied successfully with 'git apply'.\033[0m")
855
930
  else:
856
931
  print("Apply failed, attempting smart apply.")
857
- parsed_diffs = parse_diff_per_file(diff_text)
858
- print("Found", len(parsed_diffs), " files in diff, calling smartdiff for each file concurrently:")
859
-
860
- if(len(parsed_diffs) == 0):
861
- print(f"\033[1;33mThere were no entries in this diff. The LLM may have returned something invalid.\033[0m")
862
- if args.beep:
863
- print("\a") # Terminal bell for completion notification
864
- return
865
-
866
- threads = []
867
-
868
- def process_file(file_path, file_diff):
869
- full_path = Path(project_dir) / file_path
870
- print(f"Processing file: {file_path}")
871
-
872
- # Handle file deletions from diff
873
- if '+++ /dev/null' in file_diff:
874
- if full_path.exists():
875
- full_path.unlink()
876
- print(f"\033[1;32mDeleted file {file_path}.\033[0m")
877
- else:
878
- print(f"\033[1;33mFile {file_path} not found - skipping deletion\033[0m")
879
- return
880
-
881
- original_content = ''
882
- if full_path.exists():
883
- try:
884
- original_content = full_path.read_text()
885
- except UnicodeDecodeError:
886
- print(f"Skipping binary file {file_path}")
887
- return
888
-
889
- print("-" * 40)
890
- print("SMARTAPPLY")
891
- print(file_diff)
892
- print("-" * 40)
893
- if args.applymodel is None:
894
- args.applymodel = args.model
895
-
896
- try:
897
- 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)
898
-
899
- if updated_content.strip() == "":
900
- print("Cowardly refusing to write empty file to", file_path, "merge failed")
901
- return
902
-
903
- full_path.parent.mkdir(parents=True, exist_ok=True)
904
- full_path.write_text(updated_content)
905
- print(f"\033[1;32mSuccessful 'smartapply' update {file_path}.\033[0m")
906
- except Exception as e:
907
- print(f"\033[1;31mFailed to process {file_path}: {str(e)}\033[0m")
908
-
909
- threads = []
910
- for file_path, file_diff in parsed_diffs:
911
- thread = threading.Thread(
912
- target=process_file,
913
- args=(file_path, file_diff)
914
- )
915
- thread.start()
916
- threads.append(thread)
917
- for thread in threads:
918
- thread.join()
932
+ smart_apply_patch(project_dir, diff_text, user_prompt, args)
919
933
 
920
-
921
934
  if args.beep:
922
935
  print("\a") # Terminal bell for completion notification
923
936
 
@@ -927,4 +940,4 @@ def main():
927
940
  #print(f"Total cost: ${cost:.4f}")
928
941
 
929
942
  if __name__ == "__main__":
930
- main()
943
+ main()
@@ -3,9 +3,9 @@
3
3
  Command line tool to apply a unified diff directly to a file system.
4
4
 
5
5
  Usage:
6
- gptapply --diff "<diff text>"
6
+ gptpatch --diff "<diff text>"
7
7
  or
8
- gptapply path/to/diff.patch
8
+ gptpatch path/to/diff.patch
9
9
 
10
10
  This tool uses the same patch-application logic as gptdiff.
11
11
  """
@@ -37,9 +37,21 @@ def parse_arguments():
37
37
  default=".",
38
38
  help="Project directory where the diff should be applied (default: current directory)."
39
39
  )
40
+ parser.add_argument('--nobeep', action='store_false', dest='beep', default=True, help='Disable completion notification beep')
41
+ parser.add_argument(
42
+ "--model",
43
+ type=str,
44
+ default=None,
45
+ help="Model to use for applying the diff"
46
+ )
47
+ parser.add_argument(
48
+ "--max_tokens",
49
+ type=int,
50
+ default=30000,
51
+ help="Maximum tokens to use for LLM responses"
52
+ )
40
53
  return parser.parse_args()
41
54
 
42
-
43
55
  def main():
44
56
  args = parse_arguments()
45
57
  if args.diff:
@@ -56,8 +68,9 @@ def main():
56
68
  if success:
57
69
  print("✅ Diff applied successfully.")
58
70
  else:
59
- print("❌ Failed to apply diff.")
60
-
61
-
71
+ print("❌ Failed to apply diff using git apply. Attempting smart apply.")
72
+ from gptdiff.gptdiff import smart_apply_patch
73
+ smart_apply_patch(project_dir, diff_text, "", args)
74
+
62
75
  if __name__ == "__main__":
63
76
  main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gptdiff
3
- Version: 0.1.13
3
+ Version: 0.1.15
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
@@ -3,7 +3,7 @@ README.md
3
3
  setup.py
4
4
  gptdiff/__init__.py
5
5
  gptdiff/gptdiff.py
6
- gptdiff/gptdiffapply.py
6
+ gptdiff/gptpatch.py
7
7
  gptdiff.egg-info/PKG-INFO
8
8
  gptdiff.egg-info/SOURCES.txt
9
9
  gptdiff.egg-info/dependency_links.txt
@@ -1,3 +1,3 @@
1
1
  [console_scripts]
2
- gptapply = gptdiff.gptdiffapply:main
3
2
  gptdiff = gptdiff.gptdiff:main
3
+ gptpatch = gptdiff.gptpatch:main
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='gptdiff',
5
- version='0.1.13',
5
+ version='0.1.15',
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
@@ -21,7 +21,7 @@ setup(
21
21
  entry_points={
22
22
  'console_scripts': [
23
23
  'gptdiff=gptdiff.gptdiff:main',
24
- 'gptapply=gptdiff.gptdiffapply:main',
24
+ 'gptpatch=gptdiff.gptpatch:main',
25
25
  ],
26
26
  },
27
27
  license=None, # Remove license argument
File without changes
File without changes
File without changes
File without changes