gptdiff 0.1.21__py3-none-any.whl → 0.1.22__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
gptdiff/gptdiff.py CHANGED
@@ -345,7 +345,8 @@ def smartapply(diff_text, files, model=None, api_key=None, base_url=None):
345
345
  del files[path]
346
346
  else:
347
347
  updated = call_llm_for_apply_with_think_tool_available(path, original, patch, model, api_key=api_key, base_url=base_url)
348
- files[path] = updated.strip()
348
+ cleaned = strip_bad_output(updated, original)
349
+ files[path] = cleaned
349
350
 
350
351
  threads = []
351
352
 
@@ -585,7 +586,7 @@ def parse_diff_per_file(diff_text):
585
586
  for line in lines:
586
587
  if header_line_re.match(line):
587
588
  if current_file is not None and current_lines:
588
- if deletion_mode and not any(l.startswith("+++ ") for l in current_lines):
589
+ if deletion_mode and not any(l.startswith("+++ /dev/null") for l in current_lines):
589
590
  current_lines.append("+++ /dev/null")
590
591
  diffs.append((current_file, "\n".join(current_lines)))
591
592
  current_lines = [line]
@@ -778,24 +779,41 @@ def smart_apply_patch(project_dir, diff_text, user_prompt, args):
778
779
  else:
779
780
  print(f"\033[1;33mFile {file_path} not found - skipping deletion\033[0m")
780
781
  return
781
- original_content = ''
782
- if full_path.exists():
783
- try:
784
- original_content = full_path.read_text()
785
- except UnicodeDecodeError:
786
- print(f"Skipping binary file {file_path}")
787
- return
788
- if not hasattr(args, "applymodel") or args.applymodel is None:
789
- args.applymodel = args.model
790
- if args.applymodel is None:
791
- args.applymodel = os.getenv("GPTDIFF_MODEL")
782
+
783
+ try:
784
+ original_content = full_path.read_text()
785
+ except (UnicodeDecodeError, IOError):
786
+ print(f"Skipping file {file_path} due to read error")
787
+ return
788
+
789
+ # Use SMARTAPPLY-specific environment variables if set, otherwise fallback.
790
+ smart_apply_model = os.getenv("GPTDIFF_SMARTAPPLY_MODEL")
791
+ if smart_apply_model and smart_apply_model.strip():
792
+ model = smart_apply_model
793
+ elif hasattr(args, "applymodel") and args.applymodel:
794
+ model = args.applymodel
795
+ else:
796
+ model = os.getenv("GPTDIFF_MODEL", "deepseek-reasoner")
797
+
798
+ smart_api_key = os.getenv("GPTDIFF_SMARTAPPLY_API_KEY")
799
+ if smart_api_key and smart_api_key.strip():
800
+ api_key = smart_api_key
801
+ else:
802
+ api_key = os.getenv("GPTDIFF_LLM_API_KEY")
803
+
804
+ smart_base_url = os.getenv("GPTDIFF_SMARTAPPLY_BASE_URL")
805
+ if smart_base_url and smart_base_url.strip():
806
+ base_url = smart_base_url
807
+ else:
808
+ base_url = os.getenv("GPTDIFF_LLM_BASE_URL", "https://nano-gpt.com/api/v1/")
792
809
 
793
810
  print("-" * 40)
794
- print("Running smartapply with", args.applymodel,"on",file_path)
811
+ print("Running smartapply with", model, "on", file_path)
795
812
  print("-" * 40)
796
813
  try:
797
814
  updated_content = call_llm_for_apply_with_think_tool_available(
798
- file_path, original_content, file_diff, args.applymodel,
815
+ file_path, original_content, file_diff, model,
816
+ api_key=api_key, base_url=base_url,
799
817
  extra_prompt=f"This changeset is from the following instructions:\n{user_prompt}",
800
818
  max_tokens=args.max_tokens)
801
819
  if updated_content.strip() == "":
@@ -1003,5 +1021,29 @@ def swallow_reasoning(full_response: str) -> (str, str):
1003
1021
  final_content = full_response.strip()
1004
1022
  return final_content, reasoning
1005
1023
 
1024
+ def strip_bad_output(updated: str, original: str) -> str:
1025
+ """
1026
+ If the original file content does not start with a code fence but the LLM’s updated output
1027
+ starts with triple backticks (possibly with an introductory message), extract and return only
1028
+ the content within the first code block.
1029
+ """
1030
+ updated_stripped = updated.strip()
1031
+ # If the original file does not start with a code fence, but the updated output contains a code block,
1032
+ # extract and return only the content inside the first code block.
1033
+ if not original.lstrip().startswith("```"):
1034
+ # Search for the first code block in the updated output.
1035
+ m = re.search(r"```(.*?)```", updated_stripped, re.DOTALL)
1036
+ if m:
1037
+ content = m.group(1).strip()
1038
+ lines = content.splitlines()
1039
+ if len(lines) > 1:
1040
+ first_line = lines[0].strip()
1041
+ # If the first line appears to be a language specifier (i.e., a single word)
1042
+ # and is not "diff", then drop it.
1043
+ if " " not in first_line and first_line.lower() != "diff":
1044
+ content = "\n".join(lines[1:]).strip()
1045
+ return content
1046
+ return updated_stripped
1047
+
1006
1048
  if __name__ == "__main__":
1007
- main()
1049
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gptdiff
3
- Version: 0.1.21
3
+ Version: 0.1.22
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
@@ -0,0 +1,9 @@
1
+ gptdiff/__init__.py,sha256=o1hrK4GFvbfKcHPlLVArz4OunE3euIicEBYaLrdDo0k,198
2
+ gptdiff/gptdiff.py,sha256=AuZwZ1pg52RPheAzdhtZXSTjBGH4t4KRm7r9ziGHJVQ,41388
3
+ gptdiff/gptpatch.py,sha256=Z8CWWIfIL2o7xPLVdhzN5GSyJq0vsK4XQRzu4hMWNQk,2194
4
+ gptdiff-0.1.22.dist-info/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
5
+ gptdiff-0.1.22.dist-info/METADATA,sha256=_RspqYV4VPaRrpYTQXNVecFirrxzZq7MelPpZLV3O9Q,8785
6
+ gptdiff-0.1.22.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
7
+ gptdiff-0.1.22.dist-info/entry_points.txt,sha256=0VlVNr-gc04a3SZD5_qKIBbtg_L5P2x3xlKE5ftcdkc,82
8
+ gptdiff-0.1.22.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
9
+ gptdiff-0.1.22.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- gptdiff/__init__.py,sha256=o1hrK4GFvbfKcHPlLVArz4OunE3euIicEBYaLrdDo0k,198
2
- gptdiff/gptdiff.py,sha256=kDp7gDgBydfKxNm73QIT54AKnv117cZdXhRYQnfJm6A,39426
3
- gptdiff/gptpatch.py,sha256=Z8CWWIfIL2o7xPLVdhzN5GSyJq0vsK4XQRzu4hMWNQk,2194
4
- gptdiff-0.1.21.dist-info/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
5
- gptdiff-0.1.21.dist-info/METADATA,sha256=Y5O4deytuqvxRV4WaK2vAw9jFuz0OdR3Rxm3lIBNxHk,8785
6
- gptdiff-0.1.21.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
7
- gptdiff-0.1.21.dist-info/entry_points.txt,sha256=0VlVNr-gc04a3SZD5_qKIBbtg_L5P2x3xlKE5ftcdkc,82
8
- gptdiff-0.1.21.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
9
- gptdiff-0.1.21.dist-info/RECORD,,