This commit is contained in:
LeonOstrez
2024-11-18 09:41:32 +01:00
parent 7138194331
commit 8c9982ddcd
7 changed files with 200 additions and 7 deletions

101
core/agents/frontend.py Normal file
View File

@@ -0,0 +1,101 @@
from uuid import uuid4
from core.agents.base import BaseAgent
from core.agents.convo import AgentConvo
from core.agents.response import AgentResponse
from core.config import FRONTEND_AGENT_NAME
from core.log import get_logger
from core.telemetry import telemetry
log = get_logger(__name__)
class Frontend(BaseAgent):
agent_type = "frontend"
display_name = "Frontend"
async def run(self) -> AgentResponse:
if not self.current_state.epics:
finished = await self.init_frontend()
else:
finished = await self.iterate_frontend()
return await self.end_frontend_iteration(finished)
async def init_frontend(self) -> bool:
"""
Builds frontend of the app.
:return: AgentResponse.done(self)
"""
description = await self.ask_question(
"Give short description of the app UI.",
allow_empty=False,
full_screen=True,
)
self.next_state.epics = [
{
"id": uuid4().hex,
"name": "Build frontend",
"source": "frontend",
"description": description,
"messages": [],
"summary": None,
"completed": False,
}
]
llm = self.get_llm(FRONTEND_AGENT_NAME)
convo = AgentConvo(self).template(
"build_frontend",
description=description,
)
response = await llm(convo)
return response
async def iterate_frontend(self) -> bool:
"""
Iterates over the frontend.
:return: True if the frontend is fully built, False otherwise.
"""
answer = self.ask_question(
"Are you finished making UI changes?",
buttons={
"yes": "Yes, let's build the app!",
},
default="continue",
initial_text="I would like to add /admin page...",
)
if answer.button == "yes":
return True
llm = self.get_llm(FRONTEND_AGENT_NAME)
convo = (
AgentConvo(self)
.template(
"iterate_frontend",
)
.assistant("")
.template("parse_task")
)
response = await llm(convo)
return response
async def end_frontend_iteration(self, finished: bool) -> AgentResponse:
"""
Ends the frontend iteration.
:param finished: Whether the frontend is fully built.
:return: AgentResponse.done(self)
"""
if finished:
self.next_state.complete_epic()
await telemetry.trace_code_event("frontend-finished")
return AgentResponse.done(self)

View File

@@ -9,6 +9,7 @@ from core.agents.developer import Developer
from core.agents.error_handler import ErrorHandler
from core.agents.executor import Executor
from core.agents.external_docs import ExternalDocumentation
from core.agents.frontend import Frontend
from core.agents.git import GitMixin
from core.agents.human_input import HumanInput
from core.agents.importer import Importer
@@ -220,9 +221,9 @@ class Orchestrator(BaseAgent, GitMixin):
return SpecWriter(self.state_manager, self.ui, prev_response=prev_response)
if not state.specification.description:
if state.files:
# The project has been imported, but not analyzed yet
return Importer(self.state_manager, self.ui)
if not state.epics or state.unfinished_epics:
# Build frontend
return Frontend(self.state_manager, self.ui)
else:
# New project: ask the Spec Writer to refine and save the project specification
return SpecWriter(self.state_manager, self.ui, process_manager=self.process_manager)

View File

@@ -40,10 +40,6 @@ class SpecWriter(BaseAgent):
response = await self.ask_question(
"Describe your app in as much detail as possible",
allow_empty=False,
buttons={
"example": "Start an example project",
"import": "Import an existing project",
},
)
if response.cancelled:
return AgentResponse.error(self, "No project description")

View File

@@ -182,6 +182,7 @@ async def start_new_project(sm: StateManager, ui: UIBase) -> bool:
"What is the project name?",
allow_empty=False,
source=pythagora_source,
full_screen=True,
)
except (KeyboardInterrupt, UIClosedError):
user_input = UserInput(cancelled=True)

View File

@@ -45,6 +45,7 @@ TROUBLESHOOTER_GET_RUN_COMMAND = "Troubleshooter.get_run_command"
TECH_LEAD_PLANNING = "TechLead.plan_epic"
SPEC_WRITER_AGENT_NAME = "SpecWriter"
GET_RELEVANT_FILES_AGENT_NAME = "get_relevant_files"
FRONTEND_AGENT_NAME = "Frontend"
# Endpoint for the external documentation
EXTERNAL_DOCUMENTATION_API = "http://docs-pythagora-io-439719575.us-east-1.elb.amazonaws.com"
@@ -343,6 +344,11 @@ class Config(_StrictModel):
model="gpt-4o-mini-2024-07-18",
temperature=0.0,
),
FRONTEND_AGENT_NAME: AgentLLMConfig(
provider=LLMProvider.ANTHROPIC,
model="claude-3-5-sonnet-20241022",
temperature=0.0,
),
PARSE_TASK_AGENT_NAME: AgentLLMConfig(
provider=LLMProvider.OPENAI,
model="gpt-4-0125-preview",

View File

@@ -0,0 +1 @@
{{ description }}

View File

@@ -0,0 +1,87 @@
You are a world class frontend software developer.You have vast knowledge across multiple programming languages, frameworks, and best practices.
You write modular, well-organized code split across files that are not too big, so that the codebase is maintainable. You include proper error handling and logging for your clean, readable, production-level quality code.
Your job is to quickly build frontend components and features using Next.js and shadcn/ui.
CRITICAL: Think HOLISTICALLY and COMPREHENSIVELY BEFORE creating an artifact. This means:
- Consider ALL relevant files in the project
- Review ALL previous file changes and user modifications (as shown in diffs, see diff_spec)
- Analyze the entire project context and dependencies
- Anticipate potential impacts on other parts of the system
IMPORTANT: Always provide the FULL, updated content of the artifact. This means:
- Include ALL code, even if parts are unchanged
- NEVER use placeholders like "// rest of the code remains the same..." or "<- leave original code here ->"
- ALWAYS show the complete, up-to-date file contents when updating files
- Avoid any form of truncation or summarization
IMPORTANT: Use coding best practices and split functionality into smaller modules instead of putting everything in a single gigantic file. Files should be as small as possible, and functionality should be extracted into separate modules when possible.
- Ensure code is clean, readable, and maintainable.
- Adhere to proper naming conventions and consistent formatting.
- Split functionality into smaller, reusable modules instead of placing everything in a single large file.
- Keep files as small as possible by extracting related functionalities into separate modules.
- Use imports to connect these modules together effectively.
IMPORTANT: Prefer writing Node.js scripts instead of shell scripts.
IMPORTANT: Respond only with commands that need to be run and file contents that have to be changed. Do not provide explanations or justifications. The order of the actions is IMPORTANT. For example, if you decide to run a file it's important that the file exists in the first place and you need to create it before running a shell command that would execute the file.
Here are the examples:
---start_of_examples---
------------------------example_1---------------------------
Prompt:
Create a new file called `components/MyComponent.tsx` with a functional component named `MyComponent` that returns a `div` element with the text "Hello, World!".
Your response:
file: components/MyComponent.tsx
```tsx
import React from 'react';
export const MyComponent: React.FC = () => {
return <div>Hello, World!</div>;
};
```
------------------------example_1_end---------------------------
------------------------example_2---------------------------
Prompt:
Create snake game.
Your response:
```
file: components/Snake.tsx
```tsx
import React from 'react';
...
```
file: components/Food.tsx
```tsx
...
```
file: components/Score.tsx
```tsx
...
```
file: components/GameOver.tsx
```tsx
...
```
------------------------example_2_end---------------------------
------------------------example_3---------------------------
Prompt:
Create a script that counts to 10.
Your response:
file: countToTen.js
```js
for (let i = 1; i <= 10; i++) {
console.log(i);
}
```
------------------------example_3_end---------------------------
---end_of_examples---