mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-09 21:27:53 -05:00
Merge pull request #207 from Pythagora-io/feature-902-903-main
Feature 902 903 main
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import httpx
|
||||
@@ -8,7 +12,7 @@ from core.agents.git import GitMixin
|
||||
from core.agents.mixins import FileDiffMixin
|
||||
from core.agents.response import AgentResponse
|
||||
from core.cli.helpers import capture_exception
|
||||
from core.config import FRONTEND_AGENT_NAME, SWAGGER_EMBEDDINGS_API
|
||||
from core.config import FRONTEND_AGENT_NAME, IMPLEMENT_CHANGES_AGENT_NAME, SWAGGER_EMBEDDINGS_API
|
||||
from core.config.actions import (
|
||||
FE_CHANGE_REQ,
|
||||
FE_CONTINUE,
|
||||
@@ -17,7 +21,8 @@ from core.config.actions import (
|
||||
FE_ITERATION_DONE,
|
||||
FE_START,
|
||||
)
|
||||
from core.llm.parser import DescriptiveCodeBlockParser
|
||||
from core.llm.convo import Convo
|
||||
from core.llm.parser import DescriptiveCodeBlockParser, OptionalCodeBlockParser
|
||||
from core.log import get_logger
|
||||
from core.telemetry import telemetry
|
||||
from core.ui.base import ProjectStage
|
||||
@@ -25,12 +30,19 @@ from core.ui.base import ProjectStage
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
def has_correct_num_of_backticks(response: str) -> bool:
|
||||
"""
|
||||
Checks if the response has the correct number of backticks.
|
||||
"""
|
||||
return response.count("```") % 2 == 0 and response.count("```") > 0
|
||||
|
||||
|
||||
class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
agent_type = "frontend"
|
||||
display_name = "Frontend"
|
||||
|
||||
async def run(self) -> AgentResponse:
|
||||
if not self.current_state.epics[0]["messages"]:
|
||||
if not self.current_state.epics[-1]["messages"]:
|
||||
finished = await self.start_frontend()
|
||||
elif self.next_state.epics[-1].get("file_paths_to_remove_mock"):
|
||||
finished = await self.remove_mock()
|
||||
@@ -61,7 +73,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
else self.current_state.specification.template_summary,
|
||||
description=self.state_manager.template["description"]
|
||||
if self.state_manager.template is not None
|
||||
else self.next_state.epics[0]["description"],
|
||||
else self.next_state.epics[-1]["description"],
|
||||
user_feedback=None,
|
||||
first_time_build=True,
|
||||
)
|
||||
@@ -95,7 +107,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
|
||||
llm = self.get_llm(FRONTEND_AGENT_NAME)
|
||||
convo = AgentConvo(self)
|
||||
convo.messages = self.current_state.epics[0]["messages"]
|
||||
convo.messages = self.current_state.epics[-1]["messages"]
|
||||
convo.user(
|
||||
"Ok, now think carefully about your previous response. If the response ends by mentioning something about continuing with the implementation, continue but don't implement any files that have already been implemented. If your last response doesn't end by mentioning continuing, respond only with `DONE` and with nothing else."
|
||||
)
|
||||
@@ -104,12 +116,21 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
response_blocks = response.blocks
|
||||
convo.assistant(response.original_response)
|
||||
|
||||
await self.process_response(response_blocks)
|
||||
use_relace = self.current_state.epics[-1].get("use_relace", False)
|
||||
await self.process_response(response_blocks, relace=use_relace)
|
||||
|
||||
if self.next_state.epics[-1].get("manual_iteration", False):
|
||||
self.next_state.epics[-1]["fe_iteration_done"] = (
|
||||
has_correct_num_of_backticks(response.original_response)
|
||||
or self.current_state.epics[-1].get("retry_count", 0) >= 2
|
||||
)
|
||||
self.next_state.epics[-1]["retry_count"] = self.current_state.epics[-1].get("retry_count", 0) + 1
|
||||
else:
|
||||
self.next_state.epics[-1]["fe_iteration_done"] = (
|
||||
"done" in response.original_response[-20:].lower().strip() or len(convo.messages) > 15
|
||||
)
|
||||
|
||||
self.next_state.epics[-1]["messages"] = convo.messages
|
||||
self.next_state.epics[-1]["fe_iteration_done"] = (
|
||||
"done" in response.original_response[-20:].lower().strip() or len(convo.messages) > 15
|
||||
)
|
||||
self.next_state.flag_epics_as_modified()
|
||||
|
||||
return False
|
||||
@@ -120,6 +141,10 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
|
||||
:return: True if the frontend is fully built, False otherwise.
|
||||
"""
|
||||
self.next_state.epics[-1]["auto_debug_attempts"] = 0
|
||||
self.next_state.epics[-1]["retry_count"] = 0
|
||||
user_input = await self.try_auto_debug()
|
||||
|
||||
frontend_only = self.current_state.branch.project.project_type == "swagger"
|
||||
self.next_state.action = FE_ITERATION
|
||||
# update the pages in the knowledge base
|
||||
@@ -127,31 +152,33 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
|
||||
await self.ui.send_project_stage({"stage": ProjectStage.ITERATE_FRONTEND})
|
||||
|
||||
answer = await self.ask_question(
|
||||
"Do you want to change anything or report a bug?" if frontend_only else FE_CHANGE_REQ,
|
||||
buttons={"yes": "I'm done building the UI"} if not frontend_only else None,
|
||||
default="yes",
|
||||
extra_info="restart_app/collect_logs",
|
||||
placeholder='For example, "I don\'t see anything when I open http://localhost:5173/" or "Nothing happens when I click on the NEW PROJECT button"',
|
||||
)
|
||||
|
||||
if answer.button == "yes":
|
||||
if user_input:
|
||||
await self.send_message("Errors detected, fixing...")
|
||||
else:
|
||||
answer = await self.ask_question(
|
||||
FE_DONE_WITH_UI,
|
||||
buttons={
|
||||
"yes": "Yes, let's build the backend",
|
||||
"no": "No, continue working on the UI",
|
||||
},
|
||||
buttons_only=True,
|
||||
"Do you want to change anything or report a bug?" if frontend_only else FE_CHANGE_REQ,
|
||||
buttons={"yes": "I'm done building the UI"} if not frontend_only else None,
|
||||
default="yes",
|
||||
extra_info="restart_app/collect_logs",
|
||||
placeholder='For example, "I don\'t see anything when I open http://localhost:5173/" or "Nothing happens when I click on the NEW PROJECT button"',
|
||||
)
|
||||
|
||||
if answer.button == "yes":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
answer = await self.ask_question(
|
||||
FE_DONE_WITH_UI,
|
||||
buttons={
|
||||
"yes": "Yes, let's build the backend",
|
||||
"no": "No, continue working on the UI",
|
||||
},
|
||||
buttons_only=True,
|
||||
default="yes",
|
||||
)
|
||||
|
||||
await self.send_message("Implementing the changes you suggested...")
|
||||
return answer.button == "yes"
|
||||
|
||||
if answer.text:
|
||||
user_input = answer.text
|
||||
await self.send_message("Implementing the changes you suggested...")
|
||||
|
||||
llm = self.get_llm(FRONTEND_AGENT_NAME)
|
||||
|
||||
@@ -160,7 +187,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
if frontend_only:
|
||||
convo = AgentConvo(self).template(
|
||||
"is_relevant_for_docs_search",
|
||||
user_feedback=answer.text,
|
||||
user_feedback=user_input,
|
||||
)
|
||||
|
||||
response = await llm(convo)
|
||||
@@ -172,7 +199,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
async with httpx.AsyncClient(transport=httpx.AsyncHTTPTransport()) as client:
|
||||
resp = await client.post(
|
||||
url,
|
||||
json={"text": answer.text, "project_id": str(self.state_manager.project.id)},
|
||||
json={"text": user_input, "project_id": str(self.state_manager.project.id)},
|
||||
headers={"Authorization": f"Bearer {self.state_manager.get_access_token()}"},
|
||||
)
|
||||
|
||||
@@ -202,17 +229,43 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
|
||||
llm = self.get_llm(FRONTEND_AGENT_NAME, stream_output=True)
|
||||
|
||||
# try relace first
|
||||
convo = AgentConvo(self).template(
|
||||
"build_frontend",
|
||||
description=self.current_state.epics[0]["description"],
|
||||
user_feedback=answer.text,
|
||||
"iterate_frontend",
|
||||
description=self.current_state.epics[-1]["description"],
|
||||
user_feedback=user_input,
|
||||
relevant_api_documentation=relevant_api_documentation,
|
||||
first_time_build=False,
|
||||
)
|
||||
|
||||
# replace system prompt because of relace
|
||||
convo.messages[0]["content"] = AgentConvo(self).render("system_relace")
|
||||
|
||||
response = await llm(convo, parser=DescriptiveCodeBlockParser())
|
||||
|
||||
await self.process_response(response.blocks)
|
||||
relace_finished = await self.process_response(response.blocks, relace=True)
|
||||
|
||||
if not relace_finished:
|
||||
log.debug("Relace didn't finish, reverting to build_frontend")
|
||||
convo = AgentConvo(self).template(
|
||||
"build_frontend",
|
||||
description=self.current_state.epics[-1]["description"],
|
||||
user_feedback=user_input,
|
||||
relevant_api_documentation=relevant_api_documentation,
|
||||
first_time_build=False,
|
||||
)
|
||||
|
||||
response = await llm(convo, parser=DescriptiveCodeBlockParser())
|
||||
|
||||
await self.process_response(response.blocks)
|
||||
|
||||
convo.assistant(response.original_response)
|
||||
|
||||
self.next_state.epics[-1]["messages"] = convo.messages
|
||||
self.next_state.epics[-1]["use_relace"] = relace_finished
|
||||
self.next_state.epics[-1]["fe_iteration_done"] = has_correct_num_of_backticks(response.original_response)
|
||||
self.next_state.epics[-1]["manual_iteration"] = True
|
||||
self.next_state.flag_epics_as_modified()
|
||||
|
||||
return False
|
||||
|
||||
@@ -231,8 +284,8 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
await telemetry.trace_code_event(
|
||||
"frontend-finished",
|
||||
{
|
||||
"description": self.current_state.epics[0]["description"],
|
||||
"messages": self.current_state.epics[0]["messages"],
|
||||
"description": self.current_state.epics[-1]["description"],
|
||||
"messages": self.current_state.epics[-1]["messages"],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -252,7 +305,7 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
|
||||
return AgentResponse.done(self)
|
||||
|
||||
async def process_response(self, response_blocks: list, removed_mock: bool = False) -> list[str]:
|
||||
async def process_response(self, response_blocks: list, removed_mock: bool = False, relace: bool = False) -> bool:
|
||||
"""
|
||||
Processes the response blocks from the LLM.
|
||||
|
||||
@@ -276,6 +329,21 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
continue
|
||||
new_content = content
|
||||
old_content = self.current_state.get_file_content_by_path(file_path)
|
||||
|
||||
if relace:
|
||||
llm = self.get_llm(IMPLEMENT_CHANGES_AGENT_NAME)
|
||||
convo = Convo().user(
|
||||
{
|
||||
"initialCode": old_content,
|
||||
"editSnippet": new_content,
|
||||
}
|
||||
)
|
||||
|
||||
new_content = await llm(convo, temperature=0, parser=OptionalCodeBlockParser())
|
||||
|
||||
if not new_content or new_content == ("", 0, 0):
|
||||
return False
|
||||
|
||||
n_new_lines, n_del_lines = self.get_line_changes(old_content, new_content)
|
||||
await self.ui.send_file_status(file_path, "done", source=self.ui_source)
|
||||
await self.ui.generate_diff(
|
||||
@@ -306,12 +374,31 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
cmd_part = cmd_part.strip().replace("client/", "")
|
||||
command = f"{prefix} && {cmd_part}"
|
||||
|
||||
# check if cmd_part contains npm run something, if that something is not in scripts, then skip it
|
||||
if "npm run" in cmd_part:
|
||||
npm_script = cmd_part.split("npm run")[1].strip()
|
||||
|
||||
absolute_path = os.path.join(
|
||||
self.state_manager.get_full_project_root(),
|
||||
os.path.join(
|
||||
"client" if "client" in prefix else "server" if "server" in prefix else "",
|
||||
"package.json",
|
||||
),
|
||||
)
|
||||
with open(absolute_path, "r") as file:
|
||||
package_json = json.load(file)
|
||||
if npm_script not in package_json.get("scripts", {}):
|
||||
log.warning(
|
||||
f"Skipping command: {command} as npm script {npm_script} not found, command is {command}"
|
||||
)
|
||||
continue
|
||||
|
||||
await self.send_message(f"Running command: `{command}`...")
|
||||
await self.process_manager.run_command(command)
|
||||
else:
|
||||
log.info(f"Unknown block description: {description}")
|
||||
|
||||
return AgentResponse.done(self)
|
||||
return True
|
||||
|
||||
async def remove_mock(self):
|
||||
"""
|
||||
@@ -393,3 +480,65 @@ class Frontend(FileDiffMixin, GitMixin, BaseAgent):
|
||||
# self.next_state.app_link = app_link
|
||||
await self.ui.send_run_command(command)
|
||||
await self.ui.send_app_link(app_link)
|
||||
|
||||
async def kill_app(self):
|
||||
is_win = sys.platform.lower().startswith("win")
|
||||
# TODO make ports configurable
|
||||
# kill frontend - both swagger and node
|
||||
if is_win:
|
||||
await self.process_manager.run_command(
|
||||
"""for /f "tokens=5" %a in ('netstat -ano ^| findstr :5173 ^| findstr LISTENING') do taskkill /F /PID %a""",
|
||||
show_output=False,
|
||||
)
|
||||
else:
|
||||
await self.process_manager.run_command("lsof -ti:5173 | xargs -r kill", show_output=False)
|
||||
|
||||
# if node project, kill backend as well
|
||||
if self.state_manager.project.project_type == "node":
|
||||
if is_win:
|
||||
await self.process_manager.run_command(
|
||||
"""for /f "tokens=5" %a in ('netstat -ano ^| findstr :3000 ^| findstr LISTENING') do taskkill /F /PID %a""",
|
||||
show_output=False,
|
||||
)
|
||||
else:
|
||||
await self.process_manager.run_command("lsof -ti:3000 | xargs -r kill", show_output=False)
|
||||
|
||||
async def try_auto_debug(self) -> str:
|
||||
count = 3
|
||||
if self.next_state.epics[-1].get("auto_debug_attempts", 0) >= 3:
|
||||
return ""
|
||||
try:
|
||||
self.next_state.epics[-1]["auto_debug_attempts"] = (
|
||||
self.current_state.epics[-1].get("auto_debug_attempts", 0) + 1
|
||||
)
|
||||
# kill app
|
||||
await self.kill_app()
|
||||
|
||||
npm_proc = await self.process_manager.start_process("npm run start &", show_output=False)
|
||||
|
||||
while True:
|
||||
if count == 3:
|
||||
await asyncio.sleep(5)
|
||||
else:
|
||||
await asyncio.sleep(2)
|
||||
diff_stdout, diff_stderr = await npm_proc.read_output()
|
||||
if (diff_stdout == "" and diff_stderr == "") or count <= 0:
|
||||
break
|
||||
count -= 1
|
||||
|
||||
await self.process_manager.run_command("curl http://localhost:5173", show_output=False)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
diff_stdout, diff_stderr = await npm_proc.read_output()
|
||||
|
||||
# kill app again
|
||||
await self.kill_app()
|
||||
|
||||
if diff_stdout or diff_stderr:
|
||||
log.debug(f"Auto-debugging output:\n{diff_stdout}\n{diff_stderr}")
|
||||
return f"I got an error. Here are the logs:\n{diff_stdout}\n{diff_stderr}"
|
||||
except Exception as e:
|
||||
capture_exception(e)
|
||||
log.error(f"Error during auto-debugging: {e}", exc_info=True)
|
||||
|
||||
return ""
|
||||
|
||||
@@ -243,6 +243,7 @@ class DBConfig(_StrictModel):
|
||||
description="Database connection URL",
|
||||
)
|
||||
debug_sql: bool = Field(False, description="Log all SQL queries to the console")
|
||||
save_llm_requests: bool = Field(False, description="Save LLM requests to db")
|
||||
|
||||
@field_validator("url")
|
||||
@classmethod
|
||||
|
||||
@@ -31,7 +31,7 @@ class SessionManager:
|
||||
self.SessionClass = async_sessionmaker(self.engine, expire_on_commit=False)
|
||||
self.session = None
|
||||
self.recursion_depth = 0
|
||||
|
||||
self.save_llm_requests = config.save_llm_requests
|
||||
event.listen(self.engine.sync_engine, "connect", self._on_connect)
|
||||
|
||||
def _on_connect(self, dbapi_connection, _):
|
||||
|
||||
@@ -17,7 +17,7 @@ class RelaceClient(BaseLLMClient):
|
||||
def _init_client(self):
|
||||
self.headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.state_manager.get_access_token()}",
|
||||
"Authorization": f"Bearer {self.state_manager.get_access_token() if self.state_manager.get_access_token() is not None else self.config.api_key if self.config.api_key is not None else ''}",
|
||||
}
|
||||
self.client = AsyncClient()
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class LocalProcess:
|
||||
stdout: str
|
||||
stderr: str
|
||||
_process: asyncio.subprocess.Process
|
||||
show_output: bool
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
@@ -41,6 +42,7 @@ class LocalProcess:
|
||||
cwd: str = ".",
|
||||
env: dict[str, str],
|
||||
bg: bool = False,
|
||||
show_output: Optional[bool] = True,
|
||||
) -> "LocalProcess":
|
||||
log.debug(f"Starting process: {cmd} (cwd={cwd})")
|
||||
_process = await asyncio.create_subprocess_shell(
|
||||
@@ -56,13 +58,7 @@ class LocalProcess:
|
||||
_process.stdin.close()
|
||||
|
||||
return LocalProcess(
|
||||
id=uuid4(),
|
||||
cmd=cmd,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
stdout="",
|
||||
stderr="",
|
||||
_process=_process,
|
||||
id=uuid4(), cmd=cmd, cwd=cwd, env=env, stdout="", stderr="", _process=_process, show_output=show_output
|
||||
)
|
||||
|
||||
async def wait(self, timeout: Optional[float] = None) -> int:
|
||||
@@ -190,7 +186,7 @@ class ProcessManager:
|
||||
|
||||
for process in procs:
|
||||
out, err = await process.read_output()
|
||||
if self.output_handler and (out or err):
|
||||
if process.show_output and self.output_handler and (out or err):
|
||||
await self.output_handler(out, err)
|
||||
|
||||
if not process.is_running:
|
||||
@@ -210,10 +206,11 @@ class ProcessManager:
|
||||
cwd: str = ".",
|
||||
env: Optional[dict[str, str]] = None,
|
||||
bg: bool = True,
|
||||
show_output: Optional[bool] = True,
|
||||
) -> LocalProcess:
|
||||
env = {**self.default_env, **(env or {})}
|
||||
abs_cwd = abspath(join(self.root_dir, cwd))
|
||||
process = await LocalProcess.start(cmd, cwd=abs_cwd, env=env, bg=bg)
|
||||
process = await LocalProcess.start(cmd, cwd=abs_cwd, env=env, bg=bg, show_output=show_output)
|
||||
if bg:
|
||||
self.processes[process.id] = process
|
||||
return process
|
||||
|
||||
@@ -59,3 +59,5 @@ You need to write only the frontend code for this app. The backend is already fu
|
||||
{% endif %}
|
||||
|
||||
**SUPER IMPORTANT**: You must **NEVER** mention or attempt to change any files on backend (`server/` folder) or any of these frontend files: `client/src/contexts/AuthContext.tsx`, `client/src/api/api.ts`, `client/src/api/auth.ts`. Regardless of what the user asks, you must not mention these files. If you can't find a solution without changing these files, just say so.
|
||||
|
||||
**SUPER IMPORTANT**: Never write huge files, always split huge files into smaller files. For example, use React components to split the code into smaller files to make them as reusable as possible.
|
||||
63
core/prompts/frontend/iterate_frontend.prompt
Normal file
63
core/prompts/frontend/iterate_frontend.prompt
Normal file
@@ -0,0 +1,63 @@
|
||||
{% if user_feedback %}You're currently working on a frontend of an app that has the following description:
|
||||
{% else %}Create a very modern styled app with the following description:{% endif %}
|
||||
|
||||
```
|
||||
{{ description }}
|
||||
```
|
||||
|
||||
{% if summary is defined %}
|
||||
{{ summary }}
|
||||
{% elif state.specification.template_summary is defined %}
|
||||
{{ state.specification.template_summary }}
|
||||
{% endif %}
|
||||
|
||||
{% include "partials/files_list.prompt" %}
|
||||
|
||||
Use material design and nice icons for the design to be appealing and modern. Use the following libraries to make it very modern and slick:
|
||||
1. Shadcn: For the core UI components, providing modern, accessible, and customizable elements. You have already access to all components from this library inside ./src/components/ui folder, so do not modify/code them!
|
||||
2. Use lucide icons (npm install lucide-react)
|
||||
3. Heroicons: For a set of sleek, customizable icons that integrate well with modern designs.
|
||||
4. React Hook Form: For efficient form handling with minimal re-rendering, ensuring a smooth user experience in form-heavy applications.
|
||||
5. Use Tailwind built-in animations to enhance the visual appeal of the app
|
||||
6. Make the app look colorful and modern but also have the colors be subtle.
|
||||
|
||||
Choose a flat color palette and make sure that the text is readable and follow design best practices to make the text readable. Also, Implement these design features onto the page - gradient background, frosted glass effects, rounded corner, buttons need to be in the brand colors, and interactive feedback on hover and focus.
|
||||
|
||||
IMPORTANT: Text needs to be readable and in positive typography space - this is especially true for modals - they must have a bright background
|
||||
|
||||
**IMPORTANT**
|
||||
{% if first_time_build %}
|
||||
Make sure to implement all functionality (button clicks, form submissions, etc.) and use mock data for all interactions to make the app look and feel real. **ALL MOCK DATA MUST** be in the `api/` folder and it **MUST NOT** ever be hardcoded in the components.
|
||||
{% endif %}
|
||||
|
||||
The body content should not overlap with the header navigation bar or footer navigation bar or the side navigation bar.
|
||||
|
||||
|
||||
{% if user_feedback %}
|
||||
User who was using the app "{{ state.branch.project.name }}" sent you this feedback:
|
||||
```
|
||||
{{ user_feedback }}
|
||||
```
|
||||
Now, start by writing all code that's needed to fix the problem that the user reported. Think about how routes are set up, how are variables called, and other important things, and mention files by name and where should all new functionality be called from. Then, tell me all the code that needs to be written to fix this issue.
|
||||
{% else %}
|
||||
Now, start by writing all code that's needed to get the frontend built for this app. Think about how routes are set up, how are variables called, and other important things, and mention files by name and where should all new functionality be called from. Then, tell me all the code that needs to be written to implement the frontend for this app and have it fully working and all commands that need to be run.
|
||||
{% endif %}
|
||||
|
||||
IMPORTANT: When suggesting/making changes in the file you must provide full content of the file! Do not use placeholders, or comments, or truncation in any way, but instead provide the full content of the file even the parts that are unchanged!
|
||||
When you want to run a command you must put `command:` before the command and then the command itself like shown in the examples in system prompt. NEVER run `npm run start` or `npm run dev` commands, user will run them after you provide the code. The user is using {{ os }}, so the commands must run on that operating system
|
||||
|
||||
{% if relevant_api_documentation is defined %}
|
||||
|
||||
Here is relevant API documentation you need to consult and follow as close as possible.
|
||||
You need to write only the frontend code for this app. The backend is already fully built and is documented with OpenAPI specification. You don't know the API endpoints yet so you need to mock all API requests but you must mock them based on the model definitions that are known. Here are the model definitions:
|
||||
~~START_OF_API_MODEL_DEFINITIONS~~
|
||||
{{ relevant_api_documentation }}
|
||||
~~END_OF_API_MODEL_DEFINITIONS~~
|
||||
|
||||
{% endif %}
|
||||
|
||||
**SUPER IMPORTANT**: You must **NEVER** mention or attempt to change any files on backend (`server/` folder) or any of these frontend files: `client/src/contexts/AuthContext.tsx`, `client/src/api/api.ts`, `client/src/api/auth.ts`. Regardless of what the user asks, you must not mention these files. If you can't find a solution without changing these files, just say so.
|
||||
|
||||
**SUPER IMPORTANT**: Only provide the minimal code change (code difference) needed to fix the issue, never give the whole file content!
|
||||
|
||||
**SUPER IMPORTANT**: Avoid bash scripts, change code directly!
|
||||
141
core/prompts/frontend/system_relace.prompt
Normal file
141
core/prompts/frontend/system_relace.prompt
Normal file
@@ -0,0 +1,141 @@
|
||||
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 Vite for the app that user requested. Make sure to focus only on the things that are requested and do not spend time on anything else.
|
||||
|
||||
**SUPER IMPORTANT**: You must **NEVER** mention or attempt to change any files on backend (`server/` folder) or any of these frontend files: `client/src/contexts/AuthContext.tsx`, `client/src/api/api.ts`, `client/src/api/auth.ts`. Regardless of what the user asks, you must not mention these files. If you can't find a solution without changing these files, just say so.
|
||||
IMPORTANT: Think HOLISTICALLY and COMPREHENSIVELY BEFORE creating any code. 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
|
||||
|
||||
SUPER IMPORTANT: Always provide ONLY the minimal necessary code changes to fix, not full files. This means:
|
||||
If the user asks you to change something, provide only the specific lines that have been added, removed, or modified.
|
||||
Only include the specific lines that have been added, removed, or modified
|
||||
Do not write the entire file, even if most of it is unchanged.
|
||||
Focus on precise changes, not full file rewrites or summaries.
|
||||
For example, if you need to change a single line, provide only that line and its context, not the entire file content.
|
||||
|
||||
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.
|
||||
|
||||
IMPORTANT: Make sure you install all the necessary dependencies inside the correct folder. For example, if you are working on the frontend, make sure to install all the dependencies inside the "client" folder like this:
|
||||
command:
|
||||
```bash
|
||||
cd client && npm install <package-name>
|
||||
```
|
||||
NEVER run `npm run start` or `npm run dev` commands, user will run them after you provide the code.
|
||||
|
||||
IMPORTANT: The order of the actions is very 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.
|
||||
|
||||
IMPORTANT: Put full path of file you are editing! Mostly you will work with files inside "client/" folder so don't forget to put it in file path, for example DO `client/src/App.tsx` instead of `src/App.tsx`.
|
||||
|
||||
{% include "partials/file_naming.prompt" %}
|
||||
|
||||
Here are the examples:
|
||||
|
||||
---start_of_examples---
|
||||
|
||||
------------------------example_1---------------------------
|
||||
Prompt:
|
||||
Enlarge the login button.
|
||||
|
||||
Your response:
|
||||
command:
|
||||
```
|
||||
file: App.tsx
|
||||
```tsx
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-0 top-0 h-12 px-3 py-2 hover:bg-transparent"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="h-4 w-4 text-gray-500" />
|
||||
) : (
|
||||
<Eye className="h-4 w-4 text-gray-500" />
|
||||
)}
|
||||
</Button>
|
||||
```
|
||||
------------------------example_1_end---------------------------
|
||||
|
||||
------------------------example_2---------------------------
|
||||
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:
|
||||
command:
|
||||
```bash
|
||||
npm init -y
|
||||
npm install <package-name>
|
||||
```
|
||||
file: App.tsx
|
||||
```tsx
|
||||
import React from 'react';
|
||||
|
||||
export const MyComponent: React.FC = () => {
|
||||
return <div>Hello, World!</div>;
|
||||
};
|
||||
```
|
||||
------------------------example_2_end---------------------------
|
||||
|
||||
------------------------example_3---------------------------
|
||||
Prompt:
|
||||
Create snake game.
|
||||
|
||||
Your response:
|
||||
command:
|
||||
```bash
|
||||
cd client && npm install shadcn/ui
|
||||
node scripts/createInitialLeaderboard.js
|
||||
```
|
||||
file: client/components/Snake.tsx
|
||||
```tsx
|
||||
import React from 'react';
|
||||
...
|
||||
```
|
||||
file: client/components/Food.tsx
|
||||
```tsx
|
||||
...
|
||||
```
|
||||
file: client/components/Score.tsx
|
||||
```tsx
|
||||
...
|
||||
```
|
||||
file: client/components/GameOver.tsx
|
||||
```tsx
|
||||
...
|
||||
```
|
||||
------------------------example_3_end---------------------------
|
||||
|
||||
------------------------example_4---------------------------
|
||||
Prompt:
|
||||
Create a script that counts to 10.
|
||||
|
||||
Your response:
|
||||
file: countToTen.js
|
||||
```js
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
console.log(i);
|
||||
}
|
||||
```
|
||||
command:
|
||||
```bash
|
||||
node countToTen.js
|
||||
```
|
||||
------------------------example_4_end---------------------------
|
||||
|
||||
---end_of_examples---
|
||||
@@ -54,6 +54,7 @@ class StateManager:
|
||||
self.git_available = False
|
||||
self.git_used = False
|
||||
self.auto_confirm_breakdown = True
|
||||
self.save_llm_requests = False
|
||||
self.options = {}
|
||||
self.access_token = None
|
||||
self.async_tasks = None
|
||||
@@ -346,8 +347,21 @@ class StateManager:
|
||||
|
||||
:param request_log: The request log to log.
|
||||
"""
|
||||
# removed logging of LLM requests
|
||||
pass
|
||||
# Always record telemetry regardless of save_llm_requests setting
|
||||
try:
|
||||
telemetry.record_llm_request(
|
||||
request_log.prompt_tokens + request_log.completion_tokens,
|
||||
request_log.duration,
|
||||
request_log.status != LLMRequestStatus.SUCCESS,
|
||||
)
|
||||
except Exception as e:
|
||||
if self.ui:
|
||||
log.error(f"An error occurred recording telemetry: {e}")
|
||||
|
||||
# Only save to database if configured to do so
|
||||
if not self.session_manager.save_llm_requests:
|
||||
return
|
||||
|
||||
async with self.db_blocker():
|
||||
try:
|
||||
telemetry.record_llm_request(
|
||||
|
||||
Reference in New Issue
Block a user