Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions src/git_draft/bots/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
import json
import logging
from pathlib import PurePosixPath
from typing import Any, Self, TypedDict, override
from typing import Any, Self, TypedDict, cast, override

import openai

from ..common import JSONObject, UnreachableError, config_string, reindent
from ..instructions import SYSTEM_PROMPT
from .common import ActionSummary, Bot, Goal, UserFeedback, Worktree


Expand Down Expand Up @@ -159,19 +160,6 @@ def params(self) -> Sequence[openai.types.chat.ChatCompletionToolParam]:
]


# https://aider.chat/docs/more-info.html
# https://github.com/Aider-AI/aider/blob/main/aider/prompts.py
_INSTRUCTIONS = """
You are an expert software engineer, who writes correct and concise code.
Use the provided functions to find the files you need to answer the query,
read the content of the relevant ones, and save the changes you suggest.

You should stop when and ONLY WHEN all the files you need to change have
been updated. If you do not have enough information to complete your task,
use the provided tool to request it from the user, then stop.
"""


class _ToolHandler[V]:
def __init__(self, tree: Worktree, feedback: UserFeedback) -> None:
self._tree = tree
Expand Down Expand Up @@ -241,7 +229,7 @@ async def act(
tool_handler = _CompletionsToolHandler(tree, feedback)

messages: list[openai.types.chat.ChatCompletionMessageParam] = [
{"role": "system", "content": reindent(_INSTRUCTIONS)},
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": goal.prompt},
]

Expand All @@ -254,10 +242,12 @@ async def act(
tool_choice="required",
)
assert len(response.choices) == 1
choice = response.choices[0]
request_count += 1

done = True
calls = response.choices[0].message.tool_calls
messages.append(cast(Any, choice.message.to_dict(mode="json")))
calls = choice.message.tool_calls
for call in calls or []:
output = tool_handler.handle_function(call.function)
if output is not None:
Expand Down Expand Up @@ -302,7 +292,7 @@ def __init__(self, client: openai.OpenAI, model: str) -> None:
def _load_assistant_id(self) -> str:
kwargs: JSONObject = dict(
model=self._model,
instructions=reindent(_INSTRUCTIONS),
instructions=SYSTEM_PROMPT,
tools=_ToolsFactory(strict=True).params(),
)
path = self.state_folder_path(ensure_exists=True) / "ASSISTANT_ID"
Expand Down
24 changes: 24 additions & 0 deletions src/git_draft/instructions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Shared assistant prompts"""

from .common import reindent


# https://aider.chat/docs/more-info.html
# https://github.com/Aider-AI/aider/blob/main/aider/prompts.py
SYSTEM_PROMPT = reindent("""
You are an expert software engineer, who writes correct and concise code.
Use the provided functions to find the files you need to answer the query,
read the content of the relevant ones, and save the changes you suggest.

You should stop when and ONLY WHEN all the files you need to change have
been updated. If you do not have enough information to complete your task,
use the provided tool to request it from the user, then stop.
""")


OFFLINE_ANSWER = reindent("""
I'm unable to provide feedback at this time. Perform any changes you can
and await further instructions. Do not request ask me any more questions
until explicitly authorized to do so. Instead, add TODO comments in the
code where relevant.
""")
15 changes: 5 additions & 10 deletions src/git_draft/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import yaspin.core

from .bots import UserFeedback
from .common import reindent, tagged
from .common import tagged
from .events import EventConsumer, feedback_events
from .instructions import OFFLINE_ANSWER


class Progress:
Expand Down Expand Up @@ -75,22 +76,16 @@ def ask(self, question: str) -> str:
answer = self._ask(question)
if answer is None:
self.pending_question = question
answer = _offline_answer
answer = OFFLINE_ANSWER
self._event_consumer.on_event(
feedback_events.ReceiveUserGuidance(answer)
)
return _offline_answer
return answer

def _ask(self, question: str) -> str | None:
raise NotImplementedError()


_offline_answer = reindent("""
I'm unable to provide feedback at this time. Perform any final changes and
await further instructions.
""")


class _DynamicProgress(Progress):
def __init__(self) -> None:
self._spinner: _DynamicProgressSpinner | None = None
Expand Down Expand Up @@ -191,4 +186,4 @@ def _notify(self, update: str) -> None:
@override
def _ask(self, question: str) -> str | None:
self._progress.report(f"Feedback requested: {question}")
return _offline_answer
return OFFLINE_ANSWER