diff --git a/src/git_draft/bots/openai.py b/src/git_draft/bots/openai.py index 3beb12c..ec696c1 100644 --- a/src/git_draft/bots/openai.py +++ b/src/git_draft/bots/openai.py @@ -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 @@ -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 @@ -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}, ] @@ -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: @@ -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" diff --git a/src/git_draft/instructions.py b/src/git_draft/instructions.py new file mode 100644 index 0000000..50b3691 --- /dev/null +++ b/src/git_draft/instructions.py @@ -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. +""") diff --git a/src/git_draft/progress.py b/src/git_draft/progress.py index 42db6aa..d77bd3f 100644 --- a/src/git_draft/progress.py +++ b/src/git_draft/progress.py @@ -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: @@ -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 @@ -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