This commit is contained in:
Paul Gauthier 2023-06-27 18:34:52 -07:00
parent e24d03060d
commit 582143d563
5 changed files with 193 additions and 2 deletions

View file

@ -1,6 +1,7 @@
from .base_coder import Coder
from .editblock_coder import EditBlockCoder
from .editblock_func_coder import EditBlockFunctionCoder
from .wholefile_coder import WholeFileCoder
from .wholefile_func_coder import WholeFileFunctionCoder
__all__ = [Coder, EditBlockCoder, WholeFileCoder, WholeFileFunctionCoder]
__all__ = [Coder, EditBlockCoder, WholeFileCoder, WholeFileFunctionCoder, EditBlockFunctionCoder]

View file

@ -55,7 +55,12 @@ class Coder:
openai_api_base="https://api.openai.com/v1",
**kwargs,
):
from . import EditBlockCoder, WholeFileCoder, WholeFileFunctionCoder
from . import (
EditBlockCoder,
EditBlockFunctionCoder,
WholeFileCoder,
WholeFileFunctionCoder,
)
openai.api_key = openai_api_key
openai.api_base = openai_api_base
@ -81,6 +86,8 @@ class Coder:
return WholeFileCoder(main_model, io, **kwargs)
elif edit_format == "whole-func":
return WholeFileFunctionCoder(main_model, io, **kwargs)
elif edit_format == "diff-func":
return EditBlockFunctionCoder(main_model, io, **kwargs)
else:
raise ValueError(f"Unknown edit format {edit_format}")
@ -559,6 +566,8 @@ class Coder:
if functions is not None:
kwargs["functions"] = self.functions
dump(kwargs)
# Generate SHA1 hash of kwargs and append it to chat_completion_call_hashes
hash_object = hashlib.sha1(json.dumps(kwargs, sort_keys=True).encode())
self.chat_completion_call_hashes.append(hash_object.hexdigest())

View file

@ -0,0 +1,153 @@
import os
from aider import diffs
from ..dump import dump # noqa: F401
from .base_coder import Coder
from .editblock_func_prompts import EditBlockFunctionPrompts
class EditBlockFunctionCoder(Coder):
functions = [
dict(
name="replace_lines",
description="create or update one or more files",
parameters=dict(
type="object",
required=["explanation", "edits"],
properties=dict(
explanation=dict(
type="string",
description=(
"Step by step plan for the changes to be made to the code (future"
" tense, markdown format)"
),
),
edits=dict(
type="array",
items=dict(
parameters=dict(
type="object",
required=["path", "original_lines", "updated_lines"],
properties=dict(
file_path=dict(
type="string",
description="Path of file to edit",
),
original_lines=dict(
type="string",
description=(
(
"Portion of the original file, including all"
" whitespace, newlines, without skipping any lines"
),
),
),
updated_lines=dict(
type="string",
description=(
"New content to replace the `original_lines` with"
),
),
),
),
),
),
),
),
),
]
def __init__(self, *args, **kwargs):
self.gpt_prompts = EditBlockFunctionPrompts()
super().__init__(*args, **kwargs)
def update_cur_messages(self, content, edited):
if edited:
self.cur_messages += [
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
]
else:
self.cur_messages += [dict(role="assistant", content=content)]
def get_context_from_history(self, history):
context = ""
if history:
context += "# Context:\n"
for msg in history:
if msg["role"] == "user":
context += msg["role"].upper() + ": " + msg["content"] + "\n"
return context
def render_incremental_response(self, final=False):
if self.partial_response_content:
return self.partial_response_content
args = self.parse_partial_args()
if not args:
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)
return res
def live_diffs(self, fname, content, final):
lines = content.splitlines(keepends=True)
# ending an existing block
full_path = os.path.abspath(os.path.join(self.root, fname))
with open(full_path, "r") as f:
orig_lines = f.readlines()
show_diff = diffs.diff_partial_update(
orig_lines,
lines,
final,
fname=fname,
).splitlines()
return "\n".join(show_diff)
def update_files(self):
name = self.partial_response_function_call.get("name")
if name and name != "replace_lines":
raise ValueError(f'Unknown function_call name="{name}", use name="write_file"')
args = self.parse_partial_args()
if not args:
return
files = args.get("files", [])
edited = set()
for file_upd in files:
path = file_upd.get("path")
if not path:
raise ValueError(f"Missing path parameter: {file_upd}")
content = file_upd.get("content")
if not content:
raise ValueError(f"Missing content parameter: {file_upd}")
if self.allowed_to_edit(path, content):
edited.add(path)
return edited

View file

@ -0,0 +1,27 @@
# flake8: noqa: E501
from .base_prompts import CoderPrompts
class EditBlockFunctionPrompts(CoderPrompts):
main_system = """Act as an expert software developer.
Take requests for changes to the supplied code.
If the request is ambiguous, ask questions.
Once you understand the request you MUST use the `replace_lines` function to edit the files to make the needed changes.
"""
system_reminder = """
ONLY return code using the `replace_lines` function.
NEVER return code outside the `replace_lines` function.
"""
files_content_prefix = "Here is the current content of the files:\n"
files_no_full_files = "I am not sharing any files yet."
redacted_edit_message = "No changes are needed."
repo_content_prefix = (
"Below here are summaries of other files! Do not propose changes to these *read-only*"
" files without asking me first.\n"
)

View file

@ -21,4 +21,5 @@ NEVER return code outside the `write_file` function.
redacted_edit_message = "No changes are needed."
# TODO: should this be present for using this with gpt-4?
repo_content_prefix = None