Merge branch 'main' into mixpanel

This commit is contained in:
Paul Gauthier 2024-10-30 09:40:01 -07:00
commit 068fb38a5d
181 changed files with 141428 additions and 1961 deletions

View file

@ -1,10 +1,15 @@
from .architect_coder import ArchitectCoder
from .ask_coder import AskCoder
from .base_coder import Coder
from .editblock_coder import EditBlockCoder
from .editblock_fenced_coder import EditBlockFencedCoder
from .editor_editblock_coder import EditorEditBlockCoder
from .editor_whole_coder import EditorWholeFileCoder
from .help_coder import HelpCoder
from .udiff_coder import UnifiedDiffCoder
from .wholefile_coder import WholeFileCoder
from .ask_coder import AskCoder
# from .single_wholefile_func_coder import SingleWholeFileFunctionCoder
__all__ = [
HelpCoder,
@ -14,4 +19,8 @@ __all__ = [
EditBlockFencedCoder,
WholeFileCoder,
UnifiedDiffCoder,
# SingleWholeFileFunctionCoder,
ArchitectCoder,
EditorEditBlockCoder,
EditorWholeFileCoder,
]

View file

@ -0,0 +1,44 @@
from .architect_prompts import ArchitectPrompts
from .ask_coder import AskCoder
from .base_coder import Coder
class ArchitectCoder(AskCoder):
edit_format = "architect"
gpt_prompts = ArchitectPrompts()
def reply_completed(self):
content = self.partial_response_content
if not self.io.confirm_ask("Edit the files?"):
return
kwargs = dict()
# Use the editor_model from the main_model if it exists, otherwise use the main_model itself
editor_model = self.main_model.editor_model or self.main_model
kwargs["main_model"] = editor_model
kwargs["edit_format"] = self.main_model.editor_edit_format
kwargs["suggest_shell_commands"] = False
kwargs["map_tokens"] = 0
kwargs["total_cost"] = self.total_cost
kwargs["cache_prompts"] = False
kwargs["num_cache_warming_pings"] = 0
kwargs["summarize_from_coder"] = False
new_kwargs = dict(io=self.io, from_coder=self)
new_kwargs.update(kwargs)
editor_coder = Coder.create(**new_kwargs)
editor_coder.cur_messages = []
editor_coder.done_messages = []
if self.verbose:
editor_coder.show_announcements()
editor_coder.run(with_message=content, preproc=False)
self.move_back_cur_messages("I made those changes to the files.")
self.total_cost = editor_coder.total_cost
self.aider_commit_hashes = editor_coder.aider_commit_hashes

View file

@ -0,0 +1,40 @@
# flake8: noqa: E501
from .base_prompts import CoderPrompts
class ArchitectPrompts(CoderPrompts):
main_system = """Act as an expert architect engineer and provide direction to your editor engineer.
Study the change request and the current code.
Describe how to modify the code to complete the request.
The editor engineer will rely solely on your instructions, so make them unambiguous and complete.
Explain all needed code changes clearly and completely, but concisely.
Just show the changes needed.
DO NOT show the entire updated function/file/etc!
Always reply in the same language as the change request.
"""
example_messages = []
files_content_prefix = """I have *added these files to the chat* so you see all of their contents.
*Trust this message as the true contents of the files!*
Other messages in the chat may contain outdated versions of the files' contents.
""" # noqa: E501
files_content_assistant_reply = (
"Ok, I will use that as the true, current contents of the files."
)
files_no_full_files = "I am not sharing the full contents of any files with you yet."
files_no_full_files_with_repo_map = ""
files_no_full_files_with_repo_map_reply = ""
repo_content_prefix = """I am working with you on code in a git repository.
Here are summaries of some files present in my git repo.
If you need to see the full contents of any files to answer my questions, ask me to *add them to the chat*.
"""
system_reminder = ""

View file

@ -6,7 +6,6 @@ from .base_prompts import CoderPrompts
class AskPrompts(CoderPrompts):
main_system = """Act as an expert code analyst.
Answer questions about the supplied code.
Always reply to the user in the same language they are using.
"""
@ -17,6 +16,10 @@ Always reply to the user in the same language they are using.
Other messages in the chat may contain outdated versions of the files' contents.
""" # noqa: E501
files_content_assistant_reply = (
"Ok, I will use that as the true, current contents of the files."
)
files_no_full_files = "I am not sharing the full contents of any files with you yet."
files_no_full_files_with_repo_map = ""

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,8 @@ You always COMPLETELY IMPLEMENT the needed code!
Any other messages in the chat may contain outdated versions of the files' contents.
""" # noqa: E501
files_content_assistant_reply = "Ok, any changes I propose will be to those files."
files_no_full_files = "I am not sharing any files that you can edit yet."
files_no_full_files_with_repo_map = """Don't try and edit any existing code without asking me to add the files to the chat!
@ -43,3 +45,8 @@ If you need to edit any of these files, ask me to *add them to the chat* first.
read_only_files_prefix = """Here are some READ ONLY files, provided for your reference.
Do not edit these files!
"""
shell_cmd_prompt = ""
shell_cmd_reminder = ""
no_shell_cmd_prompt = ""
no_shell_cmd_reminder = ""

View file

@ -0,0 +1,64 @@
from dataclasses import dataclass, field
from typing import List
@dataclass
class ChatChunks:
system: List = field(default_factory=list)
examples: List = field(default_factory=list)
done: List = field(default_factory=list)
repo: List = field(default_factory=list)
readonly_files: List = field(default_factory=list)
chat_files: List = field(default_factory=list)
cur: List = field(default_factory=list)
reminder: List = field(default_factory=list)
def all_messages(self):
return (
self.system
+ self.examples
+ self.readonly_files
+ self.repo
+ self.done
+ self.chat_files
+ self.cur
+ self.reminder
)
def add_cache_control_headers(self):
if self.examples:
self.add_cache_control(self.examples)
else:
self.add_cache_control(self.system)
if self.repo:
# this will mark both the readonly_files and repomap chunk as cacheable
self.add_cache_control(self.repo)
else:
# otherwise, just cache readonly_files if there are any
self.add_cache_control(self.readonly_files)
self.add_cache_control(self.chat_files)
def add_cache_control(self, messages):
if not messages:
return
content = messages[-1]["content"]
if type(content) is str:
content = dict(
type="text",
text=content,
)
content["cache_control"] = {"type": "ephemeral"}
messages[-1]["content"] = [content]
def cacheable_messages(self):
messages = self.all_messages()
for i, message in enumerate(reversed(messages)):
if isinstance(message.get("content"), list) and message["content"][0].get(
"cache_control"
):
return messages[: len(messages) - i]
return messages

View file

@ -14,6 +14,7 @@ from .editblock_prompts import EditBlockPrompts
class EditBlockCoder(Coder):
"""A coder that uses search/replace blocks for code modifications."""
edit_format = "diff"
gpt_prompts = EditBlockPrompts()
@ -21,13 +22,27 @@ class EditBlockCoder(Coder):
content = self.partial_response_content
# might raise ValueError for malformed ORIG/UPD blocks
edits = list(find_original_update_blocks(content, self.fence))
edits = list(
find_original_update_blocks(
content,
self.fence,
self.get_inchat_relative_files(),
)
)
self.shell_commands += [edit[1] for edit in edits if edit[0] is None]
edits = [edit for edit in edits if edit[0] is not None]
return edits
def apply_edits(self, edits):
def apply_edits_dry_run(self, edits):
return self.apply_edits(edits, dry_run=True)
def apply_edits(self, edits, dry_run=False):
failed = []
passed = []
updated_edits = []
for edit in edits:
path, original, updated = edit
full_path = self.abs_root_path(path)
@ -39,14 +54,21 @@ class EditBlockCoder(Coder):
content = self.io.read_text(full_path)
new_content = do_replace(full_path, content, original, updated, self.fence)
if new_content:
path = self.get_rel_fname(full_path)
break
updated_edits.append((path, original, updated))
if new_content:
self.io.write_text(full_path, new_content)
if not dry_run:
self.io.write_text(full_path, new_content)
passed.append(edit)
else:
failed.append(edit)
if dry_run:
return updated_edits
if not failed:
return
@ -354,9 +376,13 @@ def do_replace(fname, content, before_text, after_text, fence=None):
return new_content
HEAD = "<<<<<<< SEARCH"
DIVIDER = "======="
UPDATED = ">>>>>>> REPLACE"
HEAD = r"^<{5,9} SEARCH\s*$"
DIVIDER = r"^={5,9}\s*$"
UPDATED = r"^>{5,9} REPLACE\s*$"
HEAD_ERR = "<<<<<<< SEARCH"
DIVIDER_ERR = "======="
UPDATED_ERR = ">>>>>>> REPLACE"
separators = "|".join([HEAD, DIVIDER, UPDATED])
@ -384,77 +410,106 @@ def strip_filename(filename, fence):
filename = filename.strip()
filename = filename.strip("`")
filename = filename.strip("*")
filename = filename.replace("\\_", "_")
# https://github.com/Aider-AI/aider/issues/1158
# filename = filename.replace("\\_", "_")
return filename
def find_original_update_blocks(content, fence=DEFAULT_FENCE):
# make sure we end with a newline, otherwise the regex will miss <<UPD on the last line
if not content.endswith("\n"):
content = content + "\n"
pieces = re.split(split_re, content)
pieces.reverse()
processed = []
# Keep using the same filename in cases where GPT produces an edit block
# without a filename.
def find_original_update_blocks(content, fence=DEFAULT_FENCE, valid_fnames=None):
lines = content.splitlines(keepends=True)
i = 0
current_filename = None
try:
while pieces:
cur = pieces.pop()
if cur in (DIVIDER, UPDATED):
processed.append(cur)
raise ValueError(f"Unexpected {cur}")
head_pattern = re.compile(HEAD)
divider_pattern = re.compile(DIVIDER)
updated_pattern = re.compile(UPDATED)
if cur.strip() != HEAD:
processed.append(cur)
continue
while i < len(lines):
line = lines[i]
processed.append(cur) # original_marker
# Check for shell code blocks
shell_starts = [
"```bash",
"```sh",
"```shell",
"```cmd",
"```batch",
"```powershell",
"```ps1",
"```zsh",
"```fish",
"```ksh",
"```csh",
"```tcsh",
]
next_is_editblock = i + 1 < len(lines) and head_pattern.match(lines[i + 1].strip())
filename = find_filename(processed[-2].splitlines(), fence)
if not filename:
if current_filename:
filename = current_filename
if any(line.strip().startswith(start) for start in shell_starts) and not next_is_editblock:
shell_content = []
i += 1
while i < len(lines) and not lines[i].strip().startswith("```"):
shell_content.append(lines[i])
i += 1
if i < len(lines) and lines[i].strip().startswith("```"):
i += 1 # Skip the closing ```
yield None, "".join(shell_content)
continue
# Check for SEARCH/REPLACE blocks
if head_pattern.match(line.strip()):
try:
# if next line after HEAD exists and is DIVIDER, it's a new file
if i + 1 < len(lines) and divider_pattern.match(lines[i + 1].strip()):
filename = find_filename(lines[max(0, i - 3) : i], fence, None)
else:
raise ValueError(missing_filename_err.format(fence=fence))
filename = find_filename(lines[max(0, i - 3) : i], fence, valid_fnames)
current_filename = filename
if not filename:
if current_filename:
filename = current_filename
else:
raise ValueError(missing_filename_err.format(fence=fence))
original_text = pieces.pop()
processed.append(original_text)
current_filename = filename
divider_marker = pieces.pop()
processed.append(divider_marker)
if divider_marker.strip() != DIVIDER:
raise ValueError(f"Expected `{DIVIDER}` not {divider_marker.strip()}")
original_text = []
i += 1
while i < len(lines) and not divider_pattern.match(lines[i].strip()):
original_text.append(lines[i])
i += 1
updated_text = pieces.pop()
processed.append(updated_text)
if i >= len(lines) or not divider_pattern.match(lines[i].strip()):
raise ValueError(f"Expected `{DIVIDER_ERR}`")
updated_marker = pieces.pop()
processed.append(updated_marker)
if updated_marker.strip() != UPDATED:
raise ValueError(f"Expected `{UPDATED}` not `{updated_marker.strip()}")
updated_text = []
i += 1
while i < len(lines) and not (
updated_pattern.match(lines[i].strip())
or divider_pattern.match(lines[i].strip())
):
updated_text.append(lines[i])
i += 1
yield filename, original_text, updated_text
except ValueError as e:
processed = "".join(processed)
err = e.args[0]
raise ValueError(f"{processed}\n^^^ {err}")
except IndexError:
processed = "".join(processed)
raise ValueError(f"{processed}\n^^^ Incomplete SEARCH/REPLACE block.")
except Exception:
processed = "".join(processed)
raise ValueError(f"{processed}\n^^^ Error parsing SEARCH/REPLACE block.")
if i >= len(lines) or not (
updated_pattern.match(lines[i].strip())
or divider_pattern.match(lines[i].strip())
):
raise ValueError(f"Expected `{UPDATED_ERR}` or `{DIVIDER_ERR}`")
yield filename, "".join(original_text), "".join(updated_text)
except ValueError as e:
processed = "".join(lines[: i + 1])
err = e.args[0]
raise ValueError(f"{processed}\n^^^ {err}")
i += 1
def find_filename(lines, fence):
def find_filename(lines, fence, valid_fnames):
"""
Deepseek Coder v2 has been doing this:
@ -468,19 +523,54 @@ def find_filename(lines, fence):
This is a more flexible search back for filenames.
"""
if valid_fnames is None:
valid_fnames = []
# Go back through the 3 preceding lines
lines.reverse()
lines = lines[:3]
filenames = []
for line in lines:
# If we find a filename, done
filename = strip_filename(line, fence)
if filename:
return filename
filenames.append(filename)
# Only continue as long as we keep seeing fences
if not line.startswith(fence[0]):
return
break
if not filenames:
return
# pick the *best* filename found
# Check for exact match first
for fname in filenames:
if fname in valid_fnames:
return fname
# Check for partial match (basename match)
for fname in filenames:
for vfn in valid_fnames:
if fname == Path(vfn).name:
return vfn
# Perform fuzzy matching with valid_fnames
for fname in filenames:
close_matches = difflib.get_close_matches(fname, valid_fnames, n=1, cutoff=0.8)
if len(close_matches) == 1:
return close_matches[0]
# If no fuzzy match, look for a file w/extension
for fname in filenames:
if "." in fname:
return fname
if filenames:
return filenames[0]
def find_similar_lines(search_lines, content_lines, threshold=0.6):

View file

@ -111,9 +111,9 @@ class EditBlockFunctionCoder(Coder):
updated = get_arg(edit, "updated_lines")
# gpt-3.5 returns lists even when instructed to return a string!
if self.code_format == "list" or type(original) == list:
if self.code_format == "list" or type(original) is list:
original = "\n".join(original)
if self.code_format == "list" or type(updated) == list:
if self.code_format == "list" or type(updated) is list:
updated = "\n".join(updated)
if original and not original.endswith("\n"):

View file

@ -14,16 +14,45 @@ If the request is ambiguous, ask questions.
Always reply to the user in the same language they are using.
Once you understand the request you MUST:
1. Decide if you need to propose *SEARCH/REPLACE* edits to any files that haven't been added to the chat. You can create new files without asking. But if you need to propose edits to existing files not already added to the chat, you *MUST* tell the user their full path names and ask them to *add the files to the chat*. End your reply and wait for their approval. You can keep asking if you then decide you need to edit more files.
2. Think step-by-step and explain the needed changes with a numbered list of short sentences.
3. Describe each change with a *SEARCH/REPLACE block* per the examples below. All changes to files must use this *SEARCH/REPLACE block* format. ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
All changes to files must use the *SEARCH/REPLACE block* format.
1. Decide if you need to propose *SEARCH/REPLACE* edits to any files that haven't been added to the chat. You can create new files without asking!
Keep this info about the user's system in mind:
{platform}
But if you need to propose edits to existing files not already added to the chat, you *MUST* tell the user their full path names and ask them to *add the files to the chat*.
End your reply and wait for their approval.
You can keep asking if you then decide you need to edit more files.
2. Think step-by-step and explain the needed changes in a few short sentences.
3. Describe each change with a *SEARCH/REPLACE block* per the examples below.
All changes to files must use this *SEARCH/REPLACE block* format.
ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
{shell_cmd_prompt}
"""
shell_cmd_prompt = """
4. *Concisely* suggest any shell commands the user might want to run in ```bash blocks.
Just suggest shell commands this way, not example code.
Only suggest complete shell commands that are ready to execute, without placeholders.
Only suggest at most a few shell commands at a time, not more than 1-3.
Use the appropriate shell based on the user's system info:
{platform}
Examples of when to suggest shell commands:
- If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content.
- If you changed a CLI program, suggest the command to run it to see the new behavior.
- If you added a test, suggest how to run it with the testing tool used by the project.
- Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations.
- If your code changes add new dependencies, suggest the command to install them.
- Etc.
"""
no_shell_cmd_prompt = """
Keep in mind these details about the user's platform and environment:
{platform}
"""
example_messages = [
dict(
role="user",
@ -116,7 +145,7 @@ from hello import hello
system_reminder = """# *SEARCH/REPLACE block* Rules:
Every *SEARCH/REPLACE block* must use this format:
1. The file path alone on a line, verbatim. No bold asterisks, no quotes around it, no escaping of characters, etc.
1. The *FULL* file path alone on a line, verbatim. No bold asterisks, no quotes around it, no escaping of characters, etc.
2. The opening fence and code language, eg: {fence[0]}python
3. The start of search block: <<<<<<< SEARCH
4. A contiguous chunk of lines to search for in the existing source code
@ -125,11 +154,14 @@ Every *SEARCH/REPLACE block* must use this format:
7. The end of the replace block: >>>>>>> REPLACE
8. The closing fence: {fence[1]}
Use the *FULL* file path, as shown to you by the user.
Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup.
*SEARCH/REPLACE* blocks will replace *all* matching occurrences.
Include enough lines to make the SEARCH blocks uniquely match the lines to change.
*SEARCH/REPLACE* blocks will *only* replace the first match occurrence.
Including multiple unique *SEARCH/REPLACE* blocks if needed.
Include enough lines in each SEARCH section to uniquely match each set of lines that need to change.
Keep *SEARCH/REPLACE* blocks concise.
Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
@ -140,11 +172,27 @@ Only create *SEARCH/REPLACE* blocks for files that the user has added to the cha
To move code within a file, use 2 *SEARCH/REPLACE* blocks: 1 to delete it from its current location, 1 to insert it in the new location.
Pay attention to which filenames the user wants you to edit, especially if they are asking you to create a new file.
If you want to put code in a new file, use a *SEARCH/REPLACE block* with:
- A new file path, including dir name if needed
- An empty `SEARCH` section
- The new file's contents in the `REPLACE` section
To rename files which have been added to the chat, use shell commands at the end of your response.
{lazy_prompt}
ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
{shell_cmd_reminder}
"""
shell_cmd_reminder = """
Examples of when to suggest shell commands:
- If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content.
- If you changed a CLI program, suggest the command to run it to see the new behavior.
- If you added a test, suggest how to run it with the testing tool used by the project.
- Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations.
- If your code changes add new dependencies, suggest the command to install them.
- Etc.
"""

View file

@ -0,0 +1,7 @@
from .editblock_coder import EditBlockCoder
from .editor_editblock_prompts import EditorEditBlockPrompts
class EditorEditBlockCoder(EditBlockCoder):
edit_format = "editor-diff"
gpt_prompts = EditorEditBlockPrompts()

View file

@ -0,0 +1,16 @@
# flake8: noqa: E501
from .editblock_prompts import EditBlockPrompts
class EditorEditBlockPrompts(EditBlockPrompts):
main_system = """Act as an expert software developer who edits source code.
{lazy_prompt}
Describe each change with a *SEARCH/REPLACE block* per the examples below.
All changes to files must use this *SEARCH/REPLACE block* format.
ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
"""
shell_cmd_prompt = ""
no_shell_cmd_prompt = ""
shell_cmd_reminder = ""

View file

@ -0,0 +1,7 @@
from .editor_whole_prompts import EditorWholeFilePrompts
from .wholefile_coder import WholeFileCoder
class EditorWholeFileCoder(WholeFileCoder):
edit_format = "editor-whole"
gpt_prompts = EditorWholeFilePrompts()

View file

@ -0,0 +1,10 @@
# flake8: noqa: E501
from .wholefile_prompts import WholeFilePrompts
class EditorWholeFilePrompts(WholeFilePrompts):
main_system = """Act as an expert software developer and make changes to source code.
{lazy_prompt}
Output a copy of each file that needs changes.
"""

View file

@ -484,7 +484,7 @@ def git_cherry_pick_osr_onto_o(texts):
# cherry pick R onto original
try:
repo.git.cherry_pick(replace_hash, "--minimal")
except git.exc.GitCommandError:
except (git.exc.ODBError, git.exc.GitError):
# merge conflicts!
return
@ -522,7 +522,7 @@ def git_cherry_pick_sr_onto_so(texts):
# cherry pick replace onto original
try:
repo.git.cherry_pick(replace_hash, "--minimal")
except git.exc.GitCommandError:
except (git.exc.ODBError, git.exc.GitError):
# merge conflicts!
return

View file

@ -6,13 +6,15 @@ from .single_wholefile_func_prompts import SingleWholeFileFunctionPrompts
class SingleWholeFileFunctionCoder(Coder):
edit_format = "func"
functions = [
dict(
name="write_file",
description="write new content into the file",
# strict=True,
parameters=dict(
type="object",
required=["explanation", "content"],
properties=dict(
explanation=dict(
type="string",
@ -26,12 +28,13 @@ class SingleWholeFileFunctionCoder(Coder):
description="Content to write to the file",
),
),
required=["explanation", "content"],
additionalProperties=False,
),
),
]
def __init__(self, *args, **kwargs):
raise RuntimeError("Deprecated, needs to be refactored to support get_edits/apply_edits")
self.gpt_prompts = SingleWholeFileFunctionPrompts()
super().__init__(*args, **kwargs)
@ -44,33 +47,19 @@ class SingleWholeFileFunctionCoder(Coder):
self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
def render_incremental_response(self, final=False):
res = ""
if self.partial_response_content:
return self.partial_response_content
res += self.partial_response_content
args = self.parse_partial_args()
return str(args)
if not args:
return
return ""
explanation = args.get("explanation")
files = args.get("files", [])
res = ""
if explanation:
res += f"{explanation}\n\n"
for i, file_upd in enumerate(files):
path = file_upd.get("path")
if not path:
continue
content = file_upd.get("content")
if not content:
continue
this_final = (i < len(files) - 1) or final
res += self.live_diffs(path, content, this_final)
for k, v in args.items():
res += "\n"
res += f"{k}:\n"
res += v
return res
@ -95,18 +84,19 @@ class SingleWholeFileFunctionCoder(Coder):
return "\n".join(show_diff)
def _update_files(self):
name = self.partial_response_function_call.get("name")
if name and name != "write_file":
raise ValueError(f'Unknown function_call name="{name}", use name="write_file"')
def get_edits(self):
chat_files = self.get_inchat_relative_files()
assert len(chat_files) == 1, chat_files
args = self.parse_partial_args()
if not args:
return
return []
content = args["content"]
path = self.get_inchat_relative_files()[0]
if self.allowed_to_edit(path, content):
return set([path])
res = chat_files[0], args["content"]
dump(res)
return [res]
return set()
def apply_edits(self, edits):
for path, content in edits:
full_path = self.abs_root_path(path)
self.io.write_text(full_path, content)

View file

@ -9,17 +9,10 @@ from .wholefile_prompts import WholeFilePrompts
class WholeFileCoder(Coder):
"""A coder that operates on entire files for code modifications."""
edit_format = "whole"
gpt_prompts = WholeFilePrompts()
def update_cur_messages(self, edited):
if edited:
self.cur_messages += [
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
]
else:
self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
def render_incremental_response(self, final):
try:
return self.get_edits(mode="diff")
@ -65,6 +58,12 @@ class WholeFileCoder(Coder):
fname = fname.strip("*") # handle **filename.py**
fname = fname.rstrip(":")
fname = fname.strip("`")
fname = fname.lstrip("#")
fname = fname.strip()
# Issue #1232
if len(fname) > 250:
fname = ""
# Did gpt prepend a bogus dir? It especially likes to
# include the path/to prefix from the one-shot example in
@ -130,15 +129,16 @@ class WholeFileCoder(Coder):
def do_live_diff(self, full_path, new_lines, final):
if Path(full_path).exists():
orig_lines = self.io.read_text(full_path).splitlines(keepends=True)
orig_lines = self.io.read_text(full_path)
if orig_lines is not None:
orig_lines = orig_lines.splitlines(keepends=True)
show_diff = diffs.diff_partial_update(
orig_lines,
new_lines,
final=final,
).splitlines()
output = show_diff
else:
output = ["```"] + new_lines + ["```"]
show_diff = diffs.diff_partial_update(
orig_lines,
new_lines,
final=final,
).splitlines()
return show_diff
output = ["```"] + new_lines + ["```"]
return output

View file

@ -52,7 +52,7 @@ path/to/filename.js
{fence[1]}
Every *file listing* MUST use this format:
- First line: the filename with any originally provided path
- First line: the filename with any originally provided path; no extra markup, punctuation, comments, etc. **JUST** the filename with path.
- Second line: opening {fence[0]}
- ... entire content of the file ...
- Final line: closing {fence[1]}