gptdiff 0.1.21__py3-none-any.whl → 0.1.22__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 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,,