gptdiff 0.1.15__tar.gz → 0.1.18__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.15
3
+ Version: 0.1.18
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
@@ -10,7 +10,7 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE.txt
11
11
  Requires-Dist: openai>=1.0.0
12
12
  Requires-Dist: tiktoken>=0.5.0
13
- Requires-Dist: ai_agent_toolbox>=0.1.11
13
+ Requires-Dist: ai_agent_toolbox>=0.1.13
14
14
  Provides-Extra: test
15
15
  Requires-Dist: pytest; extra == "test"
16
16
  Requires-Dist: pytest-mock; extra == "test"
@@ -175,7 +175,38 @@ OpenAI will not be called unless you specify `--call` or `--apply`
175
175
 
176
176
  Prevent files being appended to the prompt by adding them to `.gitignore` or `.gptignore`
177
177
 
178
- ### Command Line Usage
178
+ ## Command Line Usage
179
+
180
+ ## gptpatch: Apply Diffs Directly
181
+
182
+ `gptpatch` is a companion command-line tool to [GPTDiff](https://github.com/255BITS/gptdiff) that applies unified diffs directly to your project.
183
+
184
+ ### Usage
185
+
186
+ Apply a diff provided directly:
187
+
188
+ ```bash
189
+ gptpatch --diff "<diff text>"
190
+ ```
191
+
192
+ Or apply a diff from a file:
193
+
194
+ ```bash
195
+ gptpatch path/to/diff.patch
196
+ ```
197
+
198
+ ### Options
199
+
200
+ - **--project-dir**: Specify the target project directory (default: current directory)
201
+ - **--model**: (Optional) Specify the LLM model for advanced conflict resolution
202
+ - **--max_tokens**: (Optional) Define the maximum token count for LLM responses during patch application
203
+ - **--nobeep**: Disable the completion beep notification
204
+
205
+ ### Workflow
206
+
207
+ `gptpatch` first attempts to apply the diff using standard patch logic. If that fails, it automatically falls back to a smart apply mechanism that leverages AI-powered conflict resolution.
208
+
209
+ For more details, see the [gptpatch documentation](https://gptdiff.255labs.xyz) on our docs site.
179
210
 
180
211
  After installing the package, use the `gptdiff` command in your terminal. Change directory into your codebase and run:
181
212
 
@@ -148,7 +148,38 @@ OpenAI will not be called unless you specify `--call` or `--apply`
148
148
 
149
149
  Prevent files being appended to the prompt by adding them to `.gitignore` or `.gptignore`
150
150
 
151
- ### Command Line Usage
151
+ ## Command Line Usage
152
+
153
+ ## gptpatch: Apply Diffs Directly
154
+
155
+ `gptpatch` is a companion command-line tool to [GPTDiff](https://github.com/255BITS/gptdiff) that applies unified diffs directly to your project.
156
+
157
+ ### Usage
158
+
159
+ Apply a diff provided directly:
160
+
161
+ ```bash
162
+ gptpatch --diff "<diff text>"
163
+ ```
164
+
165
+ Or apply a diff from a file:
166
+
167
+ ```bash
168
+ gptpatch path/to/diff.patch
169
+ ```
170
+
171
+ ### Options
172
+
173
+ - **--project-dir**: Specify the target project directory (default: current directory)
174
+ - **--model**: (Optional) Specify the LLM model for advanced conflict resolution
175
+ - **--max_tokens**: (Optional) Define the maximum token count for LLM responses during patch application
176
+ - **--nobeep**: Disable the completion beep notification
177
+
178
+ ### Workflow
179
+
180
+ `gptpatch` first attempts to apply the diff using standard patch logic. If that fails, it automatically falls back to a smart apply mechanism that leverages AI-powered conflict resolution.
181
+
182
+ For more details, see the [gptpatch documentation](https://gptdiff.255labs.xyz) on our docs site.
152
183
 
153
184
  After installing the package, use the `gptdiff` command in your terminal. Change directory into your codebase and run:
154
185
 
@@ -0,0 +1,3 @@
1
+ from .gptdiff import generate_diff, smartapply, load_project_files, build_environment, save_files
2
+
3
+ __all__ = ['generate_diff', 'smartapply', 'load_project_files', 'build_environment', 'save_files']
@@ -21,7 +21,7 @@ import argparse
21
21
  import pkgutil
22
22
  import re
23
23
  import contextvars
24
- from ai_agent_toolbox import FlatXMLParser, FlatXMLPromptFormatter, Toolbox
24
+ from ai_agent_toolbox import MarkdownParser, MarkdownPromptFormatter, Toolbox, FlatXMLParser, FlatXMLPromptFormatter
25
25
  import threading
26
26
  from pkgutil import get_data
27
27
 
@@ -165,7 +165,7 @@ def load_project_files(project_dir, cwd):
165
165
  Prints skipped files to stdout for visibility
166
166
  """
167
167
  ignore_paths = [Path(cwd) / ".gitignore", Path(cwd) / ".gptignore"]
168
- gitignore_patterns = [".gitignore", "diff.patch", "prompt.txt", ".gptignore", "*.pdf", "*.docx", ".git", "*.orig", "*.rej"]
168
+ gitignore_patterns = [".gitignore", "diff.patch", "prompt.txt", ".gptignore", "*.pdf", "*.docx", ".git", "*.orig", "*.rej", "*.diff"]
169
169
 
170
170
  for p in ignore_paths:
171
171
  if p.exists():
@@ -196,8 +196,8 @@ def call_llm_for_diff(system_prompt, user_prompt, files_content, model, temperat
196
196
  enc = tiktoken.get_encoding("o200k_base")
197
197
  start_time = time.time()
198
198
 
199
- parser = FlatXMLParser("diff")
200
- formatter = FlatXMLPromptFormatter(tag="diff")
199
+ parser = MarkdownParser()
200
+ formatter = MarkdownPromptFormatter()
201
201
  toolbox = create_diff_toolbox()
202
202
  tool_prompt = formatter.usage_prompt(toolbox)
203
203
  system_prompt += "\n"+tool_prompt
@@ -273,11 +273,12 @@ prepended to the system prompt.
273
273
  with urllib.request.urlopen(prepend) as response:
274
274
  prepend = response.read().decode('utf-8')
275
275
  else:
276
- prepend = load_prepend_file(prepend)
276
+ prepend = load_prepend_file(prepend)+"\n"
277
277
  else:
278
278
  prepend = ""
279
279
 
280
- system_prompt = prepend+f"Output a git diff into a <diff> block."
280
+ diff_tag = "```diff"
281
+ system_prompt = prepend + f"Output a git diff into a \"{diff_tag}\" block."
281
282
  _, diff_text, _, _, _, _ = call_llm_for_diff(
282
283
  system_prompt,
283
284
  goal,
@@ -696,14 +697,14 @@ def call_llm_for_apply(file_path, original_content, file_diff, model, api_key=No
696
697
  4. You must return the entire file. It overwrites the existing file."""
697
698
  user_prompt = f"""File: {file_path}
698
699
  File contents:
699
- <filecontents>
700
+ ```
700
701
  {original_content}
701
- </filecontents>
702
+ ```
702
703
 
703
704
  Diff to apply:
704
- <diff>
705
+ ```diff
705
706
  {file_diff}
706
- </diff>"""
707
+ ```"""
707
708
  if extra_prompt:
708
709
  user_prompt += f"\n\n{extra_prompt}"
709
710
  if model == "gemini-2.0-flash-thinking-exp-01-21":
@@ -810,6 +811,32 @@ def smart_apply_patch(project_dir, diff_text, user_prompt, args):
810
811
  if args.beep:
811
812
  print("\a")
812
813
 
814
+ def save_files(files_dict, target_directory):
815
+ """
816
+ Save files from a dictionary mapping relative file paths to file contents
817
+ into the specified target directory.
818
+
819
+ Args:
820
+ files_dict (dict): A dictionary where keys are file paths (relative)
821
+ and values are the corresponding file contents.
822
+ target_directory (str or Path): The directory where files will be saved.
823
+ """
824
+ target_directory = Path(target_directory)
825
+ # Create the target directory if it doesn't exist.
826
+ target_directory.mkdir(parents=True, exist_ok=True)
827
+
828
+ for file_path, content in files_dict.items():
829
+ # Create the full path for the file.
830
+ full_path = target_directory / file_path
831
+
832
+ # Ensure parent directories exist.
833
+ full_path.parent.mkdir(parents=True, exist_ok=True)
834
+
835
+ # Write the file content.
836
+ with full_path.open('w', encoding='utf-8') as f:
837
+ f.write(content)
838
+ print(f"Saved: {full_path}")
839
+
813
840
  def main():
814
841
  # Adding color support for Windows CMD
815
842
  if os.name == 'nt':
@@ -840,7 +867,7 @@ def main():
840
867
  project_files.extend(load_project_files(additional_path, project_dir))
841
868
 
842
869
  if args.prepend:
843
- prepend = args.prepend
870
+ prepend = args.prepend+"\n"
844
871
  else:
845
872
  prepend = ""
846
873
 
@@ -858,8 +885,10 @@ def main():
858
885
  # If the specified prepend path does not exist, treat the value as literal content.
859
886
  prepend = prepend
860
887
 
861
- # Prepare system prompt
862
- system_prompt = prepend + f"Output a git diff into a <diff> block."
888
+ if prepend != "":
889
+ prepend += "\n"
890
+
891
+ system_prompt = prepend + f"Output a git diff into a ```diff block"
863
892
 
864
893
  files_content = ""
865
894
  for file, content in project_files:
@@ -874,9 +903,8 @@ def main():
874
903
  args.model = os.getenv('GPTDIFF_MODEL', 'deepseek-reasoner')
875
904
 
876
905
  if not args.call and not args.apply:
877
- append = "\nInstead of using <diff> tags, use ```diff backticks."
878
906
  with open('prompt.txt', 'w') as f:
879
- f.write(full_prompt+append)
907
+ f.write(full_prompt)
880
908
  print(f"Total tokens: {token_count:5d}")
881
909
  print(f"\033[1;32mNot calling GPT-4.\033[0m") # Green color for success message
882
910
  print('Instead, wrote full prompt to prompt.txt. Use `xclip -selection clipboard < prompt.txt` then paste into chatgpt')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gptdiff
3
- Version: 0.1.15
3
+ Version: 0.1.18
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
@@ -10,7 +10,7 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE.txt
11
11
  Requires-Dist: openai>=1.0.0
12
12
  Requires-Dist: tiktoken>=0.5.0
13
- Requires-Dist: ai_agent_toolbox>=0.1.11
13
+ Requires-Dist: ai_agent_toolbox>=0.1.13
14
14
  Provides-Extra: test
15
15
  Requires-Dist: pytest; extra == "test"
16
16
  Requires-Dist: pytest-mock; extra == "test"
@@ -175,7 +175,38 @@ OpenAI will not be called unless you specify `--call` or `--apply`
175
175
 
176
176
  Prevent files being appended to the prompt by adding them to `.gitignore` or `.gptignore`
177
177
 
178
- ### Command Line Usage
178
+ ## Command Line Usage
179
+
180
+ ## gptpatch: Apply Diffs Directly
181
+
182
+ `gptpatch` is a companion command-line tool to [GPTDiff](https://github.com/255BITS/gptdiff) that applies unified diffs directly to your project.
183
+
184
+ ### Usage
185
+
186
+ Apply a diff provided directly:
187
+
188
+ ```bash
189
+ gptpatch --diff "<diff text>"
190
+ ```
191
+
192
+ Or apply a diff from a file:
193
+
194
+ ```bash
195
+ gptpatch path/to/diff.patch
196
+ ```
197
+
198
+ ### Options
199
+
200
+ - **--project-dir**: Specify the target project directory (default: current directory)
201
+ - **--model**: (Optional) Specify the LLM model for advanced conflict resolution
202
+ - **--max_tokens**: (Optional) Define the maximum token count for LLM responses during patch application
203
+ - **--nobeep**: Disable the completion beep notification
204
+
205
+ ### Workflow
206
+
207
+ `gptpatch` first attempts to apply the diff using standard patch logic. If that fails, it automatically falls back to a smart apply mechanism that leverages AI-powered conflict resolution.
208
+
209
+ For more details, see the [gptpatch documentation](https://gptdiff.255labs.xyz) on our docs site.
179
210
 
180
211
  After installing the package, use the `gptdiff` command in your terminal. Change directory into your codebase and run:
181
212
 
@@ -13,5 +13,6 @@ gptdiff.egg-info/top_level.txt
13
13
  tests/test_applydiff.py
14
14
  tests/test_applydiff_edgecases.py
15
15
  tests/test_diff_parse.py
16
+ tests/test_failing_case.py
16
17
  tests/test_parse_diff_per_file.py
17
18
  tests/test_smartapply.py
@@ -1,6 +1,6 @@
1
1
  openai>=1.0.0
2
2
  tiktoken>=0.5.0
3
- ai_agent_toolbox>=0.1.11
3
+ ai_agent_toolbox>=0.1.13
4
4
 
5
5
  [docs]
6
6
  mkdocs
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='gptdiff',
5
- version='0.1.15',
5
+ version='0.1.18',
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
@@ -12,7 +12,7 @@ setup(
12
12
  install_requires=[
13
13
  'openai>=1.0.0',
14
14
  'tiktoken>=0.5.0',
15
- 'ai_agent_toolbox>=0.1.11'
15
+ 'ai_agent_toolbox>=0.1.13'
16
16
  ],
17
17
  extras_require={
18
18
  'test': ['pytest', 'pytest-mock'],
@@ -0,0 +1,25 @@
1
+ import unittest
2
+ from gptdiff.gptdiff import smartapply
3
+
4
+ class TestSmartApplyEdgeCase(unittest.TestCase):
5
+ def test_smartapply_think_tag_stripping(self):
6
+ """
7
+ This test verifies that if an LLM response includes extraneous wrapping (e.g.
8
+ ), these tags are stripped from the final applied diff.
9
+ """
10
+ diff_text = '''diff --git a/hello.py b/hello.py
11
+ --- a/hello.py
12
+ ++++ b/hello.py
13
+ @@ -1,2 +1,5 @@
14
+ def hello():
15
+ print('Hello')
16
+ ++
17
+ ++def goodbye():
18
+ ++ print('Goodbye')'''
19
+ original_files = {"hello.py": "def hello():\n print('Hello')\n"}
20
+
21
+ from unittest.mock import patch
22
+ with patch('gptdiff.gptdiff.call_llm_for_apply', return_value="\ndef goodbye():\n print('Goodbye')"):
23
+ updated_files = smartapply(diff_text, original_files)
24
+
25
+ self.assertIn("def goodbye():", updated_files.get("hello.py", ""))
@@ -1,3 +0,0 @@
1
- from .gptdiff import generate_diff, smartapply
2
-
3
- __all__ = ['generate_diff', 'smartapply']
File without changes
File without changes
File without changes