ask for stack before starting project and add ability to regenerate plan

This commit is contained in:
LeonOstrez
2024-10-09 12:45:38 +02:00
parent e3e2eff9b8
commit a718d55d1e
9 changed files with 85 additions and 14 deletions

View File

@@ -69,9 +69,10 @@ class BaseAgent:
buttons: Optional[dict[str, str]] = None,
default: Optional[str] = None,
buttons_only: bool = False,
initial_text: Optional[str] = None,
allow_empty: bool = False,
full_screen: Optional[bool] = False,
hint: Optional[str] = None,
initial_text: Optional[str] = None,
) -> UserInput:
"""
Ask a question to the user and return the response.
@@ -85,6 +86,7 @@ class BaseAgent:
:param default: Default button to select.
:param buttons_only: Only display buttons, no text input.
:param allow_empty: Allow empty input.
:param full_screen: Show question full screen in extension.
:param hint: Text to display in a popup as a hint to the question.
:param initial_text: Initial text input.
:return: User response.
@@ -95,6 +97,7 @@ class BaseAgent:
default=default,
buttons_only=buttons_only,
allow_empty=allow_empty,
full_screen=full_screen,
hint=hint,
initial_text=initial_text,
source=self.ui_source,

View File

@@ -12,7 +12,7 @@ from core.log import get_logger
from core.telemetry import telemetry
from core.templates.example_project import EXAMPLE_PROJECTS
from core.templates.registry import PROJECT_TEMPLATES
from core.ui.base import ProjectStage, success_source
from core.ui.base import ProjectStage, UISource, success_source
log = get_logger(__name__)
@@ -206,12 +206,8 @@ class TechLead(BaseAgent):
}
for task in response.plan
]
await self.ui.send_epics_and_tasks(
self.next_state.current_epic["sub_epics"],
self.next_state.tasks,
)
else:
self.next_state.current_epic["sub_epics"] = self.next_state.current_epic["sub_epics"] + [
self.next_state.current_epic["sub_epics"] = [
{
"id": sub_epic_number,
"description": sub_epic.description,
@@ -238,11 +234,32 @@ class TechLead(BaseAgent):
]
convo.remove_last_x_messages(2)
await self.ui.send_epics_and_tasks(
self.next_state.current_epic["sub_epics"],
self.next_state.tasks,
)
await self.ui.send_message(
"Here is the full plan:",
source=UISource("Pythagora", "pythagora"),
project_state_id=str(self.current_state.id),
)
for sub_epic in self.next_state.current_epic["sub_epics"]:
await self.send_message(f"Epic {sub_epic['id']}: {sub_epic['description']}")
for task in self.next_state.tasks:
if task["sub_epic_id"] == sub_epic["id"]:
await self.send_message(f" - {task['description']}")
accept_plan = await self.ask_question(
"Do you accept the suggested plan?",
buttons={"yes": "Yes, the plan looks good", "regenerate": "Regenerate the plan"},
default="yes",
buttons_only=True,
)
if accept_plan.button == "regenerate":
return await self.plan_epic(epic)
await self.ui.send_epics_and_tasks(
self.next_state.current_epic["sub_epics"],
self.next_state.tasks,
)
await telemetry.trace_code_event(
"development-plan",
{

View File

@@ -125,6 +125,40 @@ async def start_new_project(sm: StateManager, ui: UIBase) -> bool:
:param ui: User interface.
:return: True if the project was created successfully, False otherwise.
"""
stack = await ui.ask_question(
"What do you want to use to build your app?",
allow_empty=False,
buttons={"node": "Node.js", "other": "Other (coming soon)"},
buttons_only=True,
source=pythagora_source,
full_screen=True,
)
if stack.button == "other":
language = await ui.ask_question(
"What language you want to use?",
allow_empty=False,
source=pythagora_source,
full_screen=True,
)
await telemetry.trace_code_event(
"stack-choice-other",
{"language": language.text},
)
await ui.send_message("Thank you for submitting your request to support other languages.")
return False
elif stack.button == "node":
await telemetry.trace_code_event(
"stack-choice",
{"language": "node"},
)
elif stack.button == "python":
await telemetry.trace_code_event(
"stack-choice",
{"language": "python"},
)
while True:
try:
user_input = await ui.ask_question(

View File

@@ -163,6 +163,7 @@ class UIBase:
default: Optional[str] = None,
buttons_only: bool = False,
allow_empty: bool = False,
full_screen: Optional[bool] = False,
hint: Optional[str] = None,
initial_text: Optional[str] = None,
source: Optional[UISource] = None,
@@ -178,11 +179,15 @@ class UIBase:
with the selected button or text. If the user cancels
the input, the `cancelled` attribute should be set to True.
:param project_state_id: Current project state id.
:param initial_text: Placeholder for answer in extension.
:param hint: Hint for question.
:param question: Question to ask.
:param buttons: Buttons to display (if any).
:param default: Default value (if user provides no input).
:param buttons_only: Whether to only show buttons (disallow custom text).
:param allow_empty: Whether to allow empty input.
:param full_screen: Ask question in full screen (IPC).
:param source: Source of the question (if any).
:return: User input.
"""

View File

@@ -65,6 +65,7 @@ class PlainConsoleUI(UIBase):
default: Optional[str] = None,
buttons_only: bool = False,
allow_empty: bool = False,
full_screen: Optional[bool] = False,
hint: Optional[str] = None,
initial_text: Optional[str] = None,
source: Optional[UISource] = None,

View File

@@ -254,6 +254,7 @@ class IPCClientUI(UIBase):
default: Optional[str] = None,
buttons_only: bool = False,
allow_empty: bool = False,
full_screen: Optional[bool] = False,
hint: Optional[str] = None,
initial_text: Optional[str] = None,
source: Optional[UISource] = None,
@@ -278,11 +279,19 @@ class IPCClientUI(UIBase):
buttons_str = "/".join(buttons.values())
if buttons_only:
await self._send(
MessageType.BUTTONS_ONLY, content=buttons_str, category=category, project_state_id=project_state_id
MessageType.BUTTONS_ONLY,
content=buttons_str,
category=category,
project_state_id=project_state_id,
full_screen=full_screen,
)
else:
await self._send(
MessageType.BUTTONS, content=buttons_str, category=category, project_state_id=project_state_id
MessageType.BUTTONS,
content=buttons_str,
category=category,
project_state_id=project_state_id,
full_screen=full_screen,
)
if initial_text:
# FIXME: add this to base and console and document it after merging with hint PR

View File

@@ -65,6 +65,7 @@ class VirtualUI(UIBase):
default: Optional[str] = None,
buttons_only: bool = False,
allow_empty: bool = False,
full_screen: Optional[bool] = False,
hint: Optional[str] = None,
initial_text: Optional[str] = None,
source: Optional[UISource] = None,

View File

@@ -53,6 +53,7 @@ async def test_ask_question():
initial_text=None,
source=agent.ui_source,
project_state_id=str(agent.current_state.id),
full_screen=False,
)
state_manager.log_user_input.assert_awaited_once()

View File

@@ -314,7 +314,7 @@ async def test_main(mock_Orchestrator, mock_llm_check, args, run_orchestrator, r
assert success is retval
if run_orchestrator:
ui.ask_question.assert_called_once()
assert ui.ask_question.call_count == 2
mock_Orchestrator.assert_called_once()
mock_orca.run.assert_awaited_once_with()