28 |
29 | );
30 | }
31 |
32 | export default MessageHistoryModal;
33 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@dqbd/tiktoken": "^1.0.7",
13 | "@fortawesome/fontawesome-svg-core": "^6.4.0",
14 | "@fortawesome/free-solid-svg-icons": "^6.4.0",
15 | "@fortawesome/react-fontawesome": "^0.2.0",
16 | "@reduxjs/toolkit": "^1.9.5",
17 | "@testing-library/jest-dom": "^5.17.0",
18 | "@testing-library/react": "^14.0.0",
19 | "autoprefixer": "10.4.14",
20 | "axios": "^1.4.0",
21 | "codemirror": "^5.65.13",
22 | "d3-scale": "^4.0.2",
23 | "dotenv": "^16.3.1",
24 | "monaco-editor": "^0.38.0",
25 | "next": "^13.3.0",
26 | "next-connect": "^0.13.0",
27 | "openai": "^3.3.0",
28 | "openai-streams": "^6.1.0",
29 | "postcss": "8.4.24",
30 | "prism-react-renderer": "^2.0.6",
31 | "react": "18.2.0",
32 | "react-autocomplete-hint": "^2.0.0",
33 | "react-autosuggest": "^10.1.0",
34 | "react-copy-to-clipboard": "^5.1.0",
35 | "react-dom": "18.2.0",
36 | "react-dropzone": "^14.2.3",
37 | "react-icons": "^4.10.1",
38 | "react-markdown": "^8.0.7",
39 | "react-modal": "^3.16.1",
40 | "react-monaco-editor": "^0.53.0",
41 | "react-redux": "^8.1.1",
42 | "react-select": "^5.7.4",
43 | "react-select-search": "^4.1.6",
44 | "react-syntax-highlighter": "^15.5.0",
45 | "react-testing-library": "^8.0.1",
46 | "react-tooltip": "^4.2.21",
47 | "react-virtuoso": "^4.4.0",
48 | "redux": "^4.2.1",
49 | "tailwindcss": "3.3.2",
50 | "websocket": "^1.0.34"
51 | },
52 | "devDependencies": {
53 | "eslint": "^8.43.0",
54 | "eslint-plugin-react": "^7.32.2"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/backend/tests/test_working_context.py:
--------------------------------------------------------------------------------
1 | # import unittest
2 | # from unittest.mock import Mock
3 | # from memory.memory_manager import WorkingContext
4 |
5 |
6 | # class TestWorkingContext1(unittest.TestCase):
7 | # def setUp(self):
8 | # self.db_connection = Mock()
9 | # self.db_cursor = self.db_connection.cursor.return_value
10 | # self.project_directory = "./"
11 | # self.working_context = WorkingContext(
12 | # self.db_connection, self.project_directory
13 | # )
14 |
15 | # def test_create_tables(self):
16 | # self.working_context.create_tables()
17 | # self.db_cursor.execute.assert_called()
18 |
19 | # def test_add_context(self):
20 | # test_context = "Additional context"
21 | # self.working_context.add_context(test_context)
22 | # self.db_cursor.execute.assert_called()
23 | # self.db_connection.commit.assert_called()
24 |
25 | # def test_get_context(self):
26 | # self.db_cursor.fetchall.return_value = [("Context 1",), ("Context 2",)]
27 | # context = self.working_context.get_context()
28 | # self.db_cursor.execute.assert_called()
29 | # self.assertIn("Context 1", context)
30 | # self.assertIn("Context 2", context)
31 |
32 | # def test_remove_context(self):
33 | # test_context = "Remove this context"
34 | # self.working_context.remove_context(test_context)
35 | # self.db_cursor.execute.assert_called()
36 | # self.db_connection.commit.assert_called()
37 |
38 | # def test_str_representation(self):
39 | # self.db_cursor.fetchall.return_value = [("Context 1",), ("Context 2",)]
40 | # self.working_context.get_context()
41 | # str_representation = str(self.working_context)
42 | # self.assertIn("Context 1", str_representation)
43 | # self.assertIn("Context 2", str_representation)
44 |
--------------------------------------------------------------------------------
/backend/agent/agent_functions/file_ops.py:
--------------------------------------------------------------------------------
1 | import json
2 | import uuid
3 | from instructor import OpenAISchema
4 | from pydantic import Field
5 | from agent.agent_functions.function_ops import (
6 | AddFunction,
7 | DeleteFunction,
8 | ModifyFunction,
9 | )
10 | from agent.agent_functions.class_ops import AddClass, DeleteClass, ModifyClass
11 | from agent.agent_functions.method_ops import AddMethod, DeleteMethod, ModifyMethod
12 | from agent.agent_functions.import_ops import AddImport, DeleteImport, ModifyImport
13 |
14 | # Export all the entities
15 | __all__ = [
16 | "AddFunction",
17 | "DeleteFunction",
18 | "ModifyFunction",
19 | "AddClass",
20 | "DeleteClass",
21 | "ModifyClass",
22 | "AddMethod",
23 | "DeleteMethod",
24 | "ModifyMethod",
25 | "AddImport",
26 | "DeleteImport",
27 | "ModifyImport",
28 | "VariableNameChange",
29 | ]
30 |
31 |
32 | class VariableNameChange(OpenAISchema):
33 | """
34 | Represents a request to change the name of a variable throughout the entire codebase. This operation replaces all instances of the original variable name with a new name.
35 | """
36 |
37 | original_name: str = Field(..., description="The original name of the variable.")
38 | new_name: str = Field(..., description="The new name of the variable.")
39 | id: str = str(uuid.uuid4())
40 |
41 | def to_json(self):
42 | out = dict(id=self.id, original_name=self.original_name, new_name=self.new_name)
43 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
44 |
45 |
46 | _OP_LIST = [
47 | AddImport,
48 | DeleteImport,
49 | AddFunction,
50 | DeleteFunction,
51 | AddClass,
52 | DeleteClass,
53 | AddMethod,
54 | DeleteMethod,
55 | ModifyFunction,
56 | ModifyClass,
57 | ModifyMethod,
58 | ModifyImport,
59 | VariableNameChange,
60 | ]
61 |
62 | _OP_LIST = {cls.__name__: cls for cls in _OP_LIST}
63 |
--------------------------------------------------------------------------------
/frontend/store/messages/messagesSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
2 |
3 | export const fetchMessages = createAsyncThunk('messages/fetchMessages', async () => {
4 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/get_messages?chatbox=true`);
5 | const historicalMessages = await response.json();
6 | const formattedMessages = historicalMessages.messages.map(message => ({
7 | text: message.full_content,
8 | user: message.role === 'user' ? 'human' : 'ai'
9 | }));
10 | return formattedMessages;
11 | });
12 |
13 |
14 | export const messageSlice = createSlice({
15 | name: 'messages',
16 | initialState: [],
17 | reducers: {
18 | addMessage: (state, action) => {
19 | state.push(action.payload);
20 | },
21 | addAIResponse: (state, action) => {
22 | state.push(action.payload);
23 | },
24 | addAIPartResponse: (state, action) => {
25 | if (state.length > 0) {
26 | console.log("State:", state);
27 | const lastMessage = { ...state[state.length - 1] };
28 | lastMessage.text = lastMessage.text + action.payload.text;
29 | state[state.length - 1] = lastMessage;
30 | }
31 | console.log(state);
32 | // setMessages(prevMessages => {
33 | // // console.log(prevMessages);
34 | // const lastMessage = { ...prevMessages[prevMessages.length - 1] };
35 | // lastMessage.text = lastMessage.text + content;
36 | // return [...prevMessages.slice(0, prevMessages.length - 1), lastMessage];
37 | // })
38 | },
39 | },
40 | extraReducers: (builder) => {
41 | builder.addCase(fetchMessages.fulfilled, (state, action) => {
42 | state.push(...action.payload);
43 | });
44 | },
45 | });
46 |
47 | export const { addMessage, addAIResponse, addAIPartResponse } = messageSlice.actions;
48 |
49 | export default messageSlice.reducer;
50 |
--------------------------------------------------------------------------------
/backend/agent/agent_functions/teacher.txt:
--------------------------------------------------------------------------------
1 | Date: 5/1/2024
2 | Topic:
3 | Central Question: The question should be open-ended, typically starting with "why" or "how" to encourage deep thinking and discussion.
4 |
5 |
6 | 1. Engage with Initial Answers
7 | Encourage Responses: Allow participants to provide their initial thoughts or answers to the question.
8 | Listen Actively: Understand where each participant is coming from in their reasoning.
9 | 2. Begin the Socratic Questioning
10 | Clarification Questions: Ask for elaboration or clarification to understand the reasoning behind their answers. ("What do you mean by...?")
11 | Probing Assumptions: Challenge the assumptions that underlie their statements. ("What are we assuming here?")
12 | Reason and Evidence: Probe the reasons and evidence behind their thoughts. ("Why do you think this is true?")
13 | Implications and Consequences: Explore the consequences of an answer being true. ("What happens if we accept this as true?")
14 | 3. Deepen the Inquiry
15 | Counterexamples: Present scenarios or counterexamples to test the consistency of the participants' views. ("Can you think of an instance where this wouldn't hold true?")
16 | Alternative Perspectives: Encourage consideration of different perspectives. ("How would someone with an opposing view see this?")
17 | 4. Reflect and Synthesize
18 | Summary of Insights: Periodically summarize the discussion to synthesize insights and clarify where the group has reached.
19 | Encourage Reflection: Ask participants to reflect on how their views might have changed or been reinforced by the discussion.
20 | 5. Conclude with Further Questioning
21 | Final Thoughts: Allow participants to share their final thoughts and any lingering questions.
22 | Further Inquiry: End with additional questions that arise from the dialogue, setting the stage for further exploration.
23 | 6. Evaluate the Process
24 | Feedback: Gather feedback on the effectiveness of the dialogue and the comfort level of participants with the process.
25 | Self-Assessment: Reflect on your own role in guiding the dialogue and areas for improvement.
26 |
--------------------------------------------------------------------------------
/backend/test.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import sys
3 | import time
4 | import os
5 | import traceback
6 | from logging.handlers import TimedRotatingFileHandler
7 |
8 |
9 | class PrintLogger:
10 | def __init__(self, logger, original_stdout, original_stderr):
11 | self.logger = logger
12 | self.original_stdout = original_stdout
13 | self.original_stderr = original_stderr
14 |
15 | def write(self, message):
16 | # Check if the message is an error (stderr)
17 | if message.startswith("Traceback"):
18 | # Log the traceback
19 | self.logger.error(message)
20 | # Print the traceback to stderr
21 | self.original_stderr.write(message)
22 | elif message != "\n":
23 | # Log the normal message
24 | self.logger.info(message)
25 | # Print the normal message to stdout
26 | self.original_stdout.write(message)
27 |
28 | def flush(self):
29 | pass
30 |
31 |
32 | def cleanup_logs(directory, retention_duration_secs):
33 | for filename in os.listdir(directory):
34 | filepath = os.path.join(directory, filename)
35 | if os.path.isfile(filepath):
36 | file_creation_time = os.path.getctime(filepath)
37 | if time.time() - file_creation_time > retention_duration_secs:
38 | os.remove(filepath)
39 | print(f"Deleted old log file: {filepath}")
40 |
41 |
42 | # Save the original stdout and stderr
43 | original_stdout = sys.stdout
44 | original_stderr = sys.stderr
45 |
46 |
47 | # Create a logger
48 | logger = logging.getLogger("MyLogger")
49 | logger.setLevel(logging.INFO)
50 |
51 | # Create handlers
52 | handler = TimedRotatingFileHandler("app.log", when="M", interval=1, backupCount=0)
53 | console_handler = logging.StreamHandler()
54 |
55 | # Create formatters and add it to handlers
56 | log_format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
57 | handler.setFormatter(log_format)
58 | console_handler.setFormatter(log_format)
59 |
60 | # Add handlers to the logger
61 | logger.addHandler(handler)
62 | logger.addHandler(console_handler)
63 |
64 | try:
65 | 1 / 0
66 | except Exception:
67 | # Log exception with traceback
68 | logger.error("An exception occurred", exc_info=True)
69 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # 🎉🎉 Welcome to Our Project! 🎉🎉
2 |
3 | First off, thank you for considering contributing to our project. It's people like you that make this project such a great one. We welcome any type of contribution, not only code. You can help with everything from documentation to testing to artwork and more!
4 |
5 | ## 🌈 How Can I Contribute? 🌈
6 |
7 | ### 🐞 Reporting Bugs 🐞
8 |
9 | If you found something that doesn't work as expected, please open an issue in the issue tracker. Oh, and remember, a bug not reported is a bug not fixed!
10 |
11 | ### 🆕 Suggesting Features 🆕
12 |
13 | If you're into this project, chances are you've got ideas. Don't be shy! We love innovative new ideas. Open an issue if you have a feature idea or suggestion.
14 |
15 | ### 📝 Documentation 📝
16 |
17 | Can't find what you're looking for in the documentation? Or maybe you found a mistake? Let us know! We strive to make our documentation clear, concise, and helpful, but sometimes we need a fresh pair of eyes.
18 |
19 | ### 💻 Code Contributions 💻
20 |
21 | Ready to dive into the code? Awesome! We've got a list of issues that could use some help. If you're new to coding, look for issues labeled `good first issue`. If you're an experienced coder, we've got challenges for you too!
22 |
23 | ## 🚀 Getting Started 🚀
24 |
25 | If you're ready to contribute, that's great! Here's how you can set up the project for development:
26 |
27 | 1. Fork the project.
28 | 2. Clone your fork (`git clone https://github.com/yourname/projectname.git`).
29 | 3. Create a branch (`git checkout -b my-branch`).
30 | 4. Make your changes.
31 | 5. Test your changes.
32 | 6. Commit your changes (`git commit -m 'Add my awesome feature'`).
33 | 7. Push to your branch (`git push origin my-branch`).
34 | 8. Open a Pull Request.
35 |
36 | ## 🎈 Code of Conduct 🎈
37 |
38 | We want to foster an inclusive and friendly community. Please follow our [Code of Conduct](CODE_OF_CONDUCT.md).
39 |
40 | ## 🎁 What's Next? 🎁
41 |
42 | Once you open a pull request, we will review your contribution and provide feedback. If everything looks good, we'll merge your changes into our project. And then... Congratulations! You will have just made this project even better!
43 |
44 | Remember, the best way to learn is to do. So, let's get started! We can't wait to see what amazing things you'll bring to this project. 🚀🌟
--------------------------------------------------------------------------------
/backend/tests/test_codebase.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import os
3 | from unittest.mock import Mock
4 | from database.my_codebase import MyCodebase
5 | from unittest.mock import patch
6 |
7 | IGNORE_DIRS = ["node_modules", ".next", ".venv", "__pycache__", ".git"]
8 | FILE_EXTENSIONS = [".js", ".py", ".md"]
9 |
10 |
11 | class MyCodebaseTests(unittest.TestCase):
12 | DIRECTORY = os.path.dirname(os.path.abspath(__file__))
13 | DIRECTORY = os.path.join(DIRECTORY, "..", "..")
14 | print(DIRECTORY)
15 |
16 | def setUp(self):
17 | # Create a mock connection object
18 | conn = Mock()
19 |
20 | # Create a mock cursor object
21 | cursor = Mock()
22 |
23 | # Configure the mock connection to return the mock cursor when cursor() is called
24 | conn.cursor.return_value = cursor
25 |
26 | # Configure the mock cursor to return an empty list when fetchall() is called
27 | cursor.fetchall.return_value = []
28 |
29 | self.codebase = MyCodebase(
30 | self.DIRECTORY,
31 | db_connection=conn,
32 | ignore_dirs=IGNORE_DIRS,
33 | file_extensions=FILE_EXTENSIONS,
34 | )
35 |
36 | @patch(
37 | "database.my_codebase.ENCODER.encode", return_value=list(range(10))
38 | ) # mocks ENCODER.encode to always return a list of length 10
39 | def test_set_directory(self, mock_encode):
40 | new_directory = os.path.abspath("../")
41 | self.codebase.set_directory(new_directory)
42 | self.assertEqual(self.codebase.directory, os.path.abspath(new_directory))
43 |
44 | @patch(
45 | "database.my_codebase.ENCODER.encode", return_value=list(range(10))
46 | ) # mocks ENCODER.encode to always return a list of length 10
47 | def test_is_valid_file(self, mock_encode):
48 | self.assertTrue(self.codebase._is_valid_file("valid_file.py"))
49 | self.assertFalse(self.codebase._is_valid_file(".invalid_file.py"))
50 | self.assertFalse(self.codebase._is_valid_file("invalid_file.json"))
51 | self.assertFalse(self.codebase._is_valid_file("package-lock.json"))
52 |
53 | @patch(
54 | "database.my_codebase.ENCODER.encode", return_value=list(range(10))
55 | ) # mocks ENCODER.encode to always return a list of length 10
56 | def test_tree(self, mock_encode):
57 | tree = self.codebase.tree()
58 | self.assertIsInstance(tree, str)
59 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Project Code of Conduct
2 |
3 | Welcome to our awesome project! We're thrilled to have you as a part of our community. To ensure a positive and inclusive environment, we have established this Code of Conduct that applies to everyone participating in our project. By contributing to this project, you agree to abide by this Code of Conduct.
4 |
5 | ## Our Pledge
6 |
7 | In the spirit of fostering an open and welcoming community, we pledge to:
8 |
9 | - Be friendly, respectful, and supportive towards all contributors.
10 | - Embrace diversity and inclusivity, and treat everyone with kindness and empathy.
11 | - Listen to and consider different perspectives and ideas.
12 | - Be patient and understanding, especially with those who are new to the project.
13 | - Encourage and empower others to participate and contribute.
14 |
15 | ## Our Standards
16 |
17 | To create a fun and exciting environment, we have set the following standards for behavior within our community:
18 |
19 | - Be positive and constructive in your interactions with others.
20 | - Use welcoming and inclusive language.
21 | - Be respectful of differing opinions and experiences.
22 | - Accept and provide constructive feedback gracefully.
23 | - Focus on collaboration and teamwork.
24 | - Be mindful of your words and actions, as they have an impact on others.
25 | - Be open to learning from others and sharing your knowledge.
26 |
27 | ## Our Responsibilities
28 |
29 | As project maintainers, we are responsible for maintaining a safe and inclusive environment. We will:
30 |
31 | - Enforce this Code of Conduct consistently and fairly.
32 | - Address any reported issues promptly and with confidentiality.
33 | - Provide guidance and support to those who seek help.
34 | - Make decisions in the best interest of the community.
35 |
36 | ## Scope
37 |
38 | This Code of Conduct applies to all project-related spaces, including but not limited to:
39 |
40 | - GitHub repositories
41 | - Issue trackers
42 | - Pull request discussions
43 | - Project meetings
44 | - Project events
45 |
46 | ## Enforcement
47 |
48 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [insert contact email address]. All complaints will be reviewed and investigated promptly and fairly. The project team is obligated to maintain the confidentiality of the reporter of an incident.
49 |
50 | ## Attribution
51 |
52 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
53 |
54 | Let's make this project an amazing place where everyone feels welcome and excited to contribute!
55 |
--------------------------------------------------------------------------------
/frontend/components/modal_bar_modals/ContextViewerModal.js:
--------------------------------------------------------------------------------
1 | // frontend/components/modal_bar_modals/ContextViewerModal.js
2 | import React, { useEffect, useState } from 'react';
3 | import ReactModal from 'react-modal';
4 | import { useDispatch, useSelector } from 'react-redux';
5 | import { setIsContextModalOpen, setWorkingContext } from '../../store/modal_bar_modals/contextViewerSlice';
6 |
7 | ReactModal.setAppElement('#__next');
8 |
9 | const ContextViewerModal = () => {
10 | const dispatch = useDispatch();
11 | const isOpen = useSelector(state => state.contextViewer.isModalOpen);
12 | const workingContext = useSelector(state => state.contextViewer.workingContext);
13 |
14 | // const currentDirectory = useSelector(state => state.sidebar.currentDirectory);
15 | const fetchContext = async () => {
16 | try {
17 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/get_context`);
18 | if (!response.ok) {
19 | throw new Error('Network response was not ok');
20 | }
21 | const data = await response.json();
22 | // Do something with the context data, e.g., store in state, display in a modal, etc.
23 | dispatch(setWorkingContext(data.context));
24 | console.log(data.context);
25 | } catch (error) {
26 | console.error('There has been a problem with your fetch operation:', error);
27 | }
28 | };
29 |
30 | useEffect(() => {
31 | if (isOpen) {
32 | fetchContext();
33 | }
34 | }, [isOpen]);
35 |
36 |
37 | return (
38 | dispatch(setIsContextModalOpen(false))}
41 | shouldCloseOnOverlayClick={true}
42 | className="fixed inset-0 flex items-center justify-center m-96 bg-gray-800 text-white border-blue-500"
43 | overlayClassName="fixed inset-0 bg-gray-800 bg-opacity-50"
44 | >
45 |
46 |
Working Context
47 |
52 |
58 |
59 |
60 | );
61 | }
62 |
63 | export default ContextViewerModal;
--------------------------------------------------------------------------------
/sweep.yaml:
--------------------------------------------------------------------------------
1 | # Sweep AI turns bugs & feature requests into code changes (https://sweep.dev)
2 | # For details on our config file, check out our docs at https://docs.sweep.dev/usage/config
3 |
4 | # This setting contains a list of rules that Sweep will check for. If any of these rules are broken in a new commit, Sweep will create an pull request to fix the broken rule.
5 | rules:
6 | - "Leftover TODOs in the code should be handled."
7 | - "All new business logic should have corresponding unit tests in the tests/ directory."
8 | - "Any clearly inefficient or repeated code should be optimized or refactored."
9 | - "Add docstrings to all functions and file headers."
10 | - "Add type annotations to all functions."
11 | - "Update the README.md file to reflect the changes in this pull request."
12 | - "Add a changelog entry to CHANGELOG.md."
13 |
14 |
15 | # This is the branch that Sweep will develop from and make pull requests to. Most people use 'main' or 'master' but some users also use 'dev' or 'staging'.
16 | branch: 'main'
17 |
18 | # By default Sweep will read the logs and outputs from your existing Github Actions. To disable this, set this to false.
19 | gha_enabled: True
20 |
21 | # This is the description of your project. It will be used by sweep when creating PRs. You can tell Sweep what's unique about your project, what frameworks you use, or anything else you want.
22 | #
23 | # Example:
24 | #
25 | # description: sweepai/sweep is a python project. The main api endpoints are in sweepai/api.py. Write code that adheres to PEP8.
26 | description: "blazickjp/GPT-CodeApp is a python 3.11 project. The backend is a FastAPI application found in the backend directory. The frontend is a next.js react app found in the frontend directory. Write code that adheres to PEP8 for Python code."
27 |
28 | # This sets whether to create pull requests as drafts. If this is set to True, then all pull requests will be created as drafts and GitHub Actions will not be triggered.
29 | draft: False
30 |
31 | # This is a list of directories that Sweep will not be able to edit.
32 | blocked_dirs: [node_modules, .git, .github, .vscode, .idea, .vs, .gitlab, .circleci, .gitignore, .gitattributes, .DS_Store, .sweep, .trunk, .pytest_cache, .mypy_cache, .cache, .venv, ]
33 |
34 | # This is a list of documentation links that Sweep will use to help it understand your code. You can add links to documentation for any packages you use here.
35 | #
36 | # Example:
37 | #
38 | # docs:
39 | # - PyGitHub: ["https://pygithub.readthedocs.io/en/latest/", "We use pygithub to interact with the GitHub API"]
40 | docs:
41 | - OpenAI-cookbook: ["https://github.com/openai/openai-cookbook", "We mostly use OpenAI's models and draw inspiration from their cookbook."]
42 | - OpenAi: ["https://platform.openai.com/docs/overview", "We use OpenAI's API to interact with the user."]
43 |
44 | # Sandbox executes commands in a sandboxed environment to validate code changes after every edit to guarantee pristine code. For more details, see the [Sandbox](./sandbox) page.
45 | sandbox:
46 | install:
47 | - trunk init
48 | - pip install -r requirements.txt
49 | check:
50 | - trunk fmt {file_path} || return 0
51 | - trunk check --fix --print-failures {file_path}
52 | - pytest
53 |
--------------------------------------------------------------------------------
/frontend/components/modal_bar_modals/FunctionsModal.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import ReactModal from 'react-modal';
4 | import { useDispatch, useSelector } from 'react-redux';
5 | import { setIsFunctionModalOpen } from '../../store/modal_bar_modals/functionsSlice';
6 |
7 | const FunctionsModal = () => {
8 | const agentFunctions = useSelector(state => state.functions.agent_functions);
9 | const agentTokens = useSelector(state => state.functions.agent_tokens);
10 | const onDemandFunctions = useSelector(state => state.functions.on_demand_functions);
11 | const onDemandTokens = useSelector(state => state.functions.onDemandTokens);
12 | const isOpen = useSelector(state => state.functions.isFunctionModalOpen);
13 | const dispatch = useDispatch();
14 |
15 | return (
16 | dispatch(setIsFunctionModalOpen(false))}
19 | shouldCloseOnOverlayClick={true}
20 | className="fixed inset-0 flex items-center justify-center w-1/2 h-1/2 mx-auto my-auto border border-gray-200 rounded"
21 | overlayClassName="fixed inset-0 bg-black bg-opacity-50"
22 | >
23 |
24 |
Agent Functions: {agentTokens} Tokens
25 |
26 |
27 | Agent functions are automatically called by the agent when needed (like OpenAI indended).
28 | You may create new functions in the
agent_functions.py
file and add them to
29 | the agent in the
setup_app.py
file.
30 |
31 |
32 | {agentFunctions?.map((f) => (
33 |
34 |
{f?.name}
35 |
{f?.description}
36 |
37 | ))}
38 |
39 |
On Demand Functions: {onDemandTokens} Tokens
40 |
41 |
42 | On Demand Functions can be run at any time and will not automatically be called by
43 | your agent. When activated they will be run on the next turn of the conversation. We find
44 | the best way to leverage on demand functions is to work within your normal chat to
45 | develop a detailed prompt. Once you have one you like, copy and paste it into the
46 | function call.
47 |
48 |
49 | {onDemandFunctions?.map((f) => (
50 |
51 |
{f?.name}
52 |
{f?.description}
53 | {/* {f?.description} */}
54 |
55 | ))}
56 |
57 |
58 | );
59 | };
60 |
61 | export default FunctionsModal;
--------------------------------------------------------------------------------
/backend/agent/agent_functions/class_ops.py:
--------------------------------------------------------------------------------
1 | from instructor import OpenAISchema
2 | from pydantic import Field
3 | import json
4 | import uuid
5 |
6 |
7 | class AddClass(OpenAISchema):
8 | """Represents a class to be added to a file."""
9 |
10 | file_name: str = Field(..., description="The name of the file to add the class to.")
11 | class_name: str = Field(..., description="The name of the class.")
12 | bases: list[str] = Field([], description="The base classes for the class.")
13 | body: str = Field(..., description="The body of the class.")
14 | decorator_list: list[str] = Field(
15 | [], description="The list of decorators to be applied to the class."
16 | )
17 | id: str | None = str(uuid.uuid4())
18 |
19 | def to_json(self):
20 | out = dict(
21 | id=self.id,
22 | file_name=self.file_name,
23 | class_name=self.class_name,
24 | bases=self.bases,
25 | body=self.body,
26 | decorator_list=self.decorator_list,
27 | )
28 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
29 |
30 |
31 | class DeleteClass(OpenAISchema):
32 | """Represents a class to be deleted.
33 |
34 | Attributes:
35 | file_name (str): The name of the file containing the class to be deleted.
36 | class_name (str): The name of the class to be deleted.
37 | """
38 |
39 | file_name: str = Field(
40 | ..., description="The name of the file containing the class to delete."
41 | )
42 | class_name: str = Field(..., description="The name of the class to delete.")
43 | id: str | None = str(uuid.uuid4())
44 |
45 | def to_json(self):
46 | out = dict(
47 | id=self.id,
48 | file_name=self.file_name,
49 | class_name=self.class_name,
50 | )
51 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
52 |
53 |
54 | class ModifyClass(OpenAISchema):
55 | """Represents a request to modify a Python class. Modifications will override the existing class."""
56 |
57 | file_name: str = Field(
58 | ..., description="The name of the file containing the class to modify."
59 | )
60 | class_name: str = Field(..., description="The name of the class to modify.")
61 | new_bases: list[str] | None = Field(
62 | None, description="The new base classes for the class."
63 | )
64 | new_body: str | None = Field(
65 | None,
66 | description="The new body of the class as a list of statements or a string. This will replace the entire existing body of the class.",
67 | )
68 | new_decorator_list: list[str] | None = Field(
69 | None, description="The new list of decorators for the class."
70 | )
71 | new_name: str | None = Field(None, description="The new name for the class.")
72 | new_args: str | None = Field(None, description="The new arguments for the class.")
73 | new_docstring: str | None = Field(
74 | None, description="The new docstring for the function."
75 | )
76 | id: str | None = str(uuid.uuid4())
77 |
78 | def to_json(self):
79 | out = dict(
80 | id=self.id,
81 | file_name=self.file_name,
82 | class_name=self.class_name,
83 | new_bases=self.new_bases,
84 | new_body=self.new_body,
85 | new_decorator_list=self.new_decorator_list,
86 | new_name=self.new_name,
87 | new_args=self.new_args,
88 | )
89 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
90 |
--------------------------------------------------------------------------------
/backend/tests/test_coding_agent.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import instructor
3 | import difflib
4 | from openai import OpenAI
5 | from unittest.mock import MagicMock, mock_open, patch, call
6 | from agent.coding_agent import CodingAgent
7 | from memory.memory_manager import MemoryManager
8 | from database.my_codebase import MyCodebase
9 | from agent.agent_functions.file_ops import _OP_LIST, AddFunction, DeleteFunction
10 |
11 |
12 | client = instructor.patch(OpenAI())
13 |
14 | IGNORE_DIRS = ["node_modules", ".next", ".venv", "__pycache__", ".git"]
15 | FILE_EXTENSIONS = [".js", ".py", ".md"]
16 |
17 | # Sample code for testing purposes
18 | ORIGINAL_CODE = "def example():\n pass\n"
19 | MODIFIED_CODE_ADD = (
20 | "def example():\n pass\n\ndef added_function():\n return 'test'\n"
21 | )
22 | MODIFIED_CODE_DELETE = ""
23 |
24 | # Sample operation instances for testing
25 | add_function_op = AddFunction(
26 | file_name="example.py",
27 | function_name="added_function",
28 | args="",
29 | body="return 'test'",
30 | decorator_list=[],
31 | )
32 | delete_function_op = DeleteFunction(file_name="example.py", function_name="example")
33 |
34 |
35 | class TestCodingAgent(unittest.TestCase):
36 | def setUp(self):
37 | # Mock the CodingAgent and its dependencies
38 | self.agent = CodingAgent(memory_manager=None, function_map=None, codebase=None)
39 | self.agent.ops_to_execute = [add_function_op, delete_function_op]
40 | # Patch the open function in the coding_agent module
41 | self.mock_open = mock_open(read_data=ORIGINAL_CODE)
42 | self.open_patch = patch("agent.coding_agent.open", self.mock_open)
43 | self.open_patch.start()
44 |
45 | def tearDown(self):
46 | self.open_patch.stop()
47 |
48 | def test_execute_ops(self):
49 | # Call the method to test
50 | diffs = self.agent.execute_ops(self.agent.ops_to_execute)
51 | print("Diff: ", diffs)
52 |
53 | # We expect two diffs: one for the addition and one for the deletion
54 | expected_diffs = [
55 | "--- before.py\n+++ after.py\n@@ -1,2 +1,6 @@\n def example():\n pass\n+\n+\n+def added_function():\n+ return 'test'\n",
56 | "--- before.py\n+++ after.py\n@@ -1,2 +0,0 @@\n-def example():\n- pass\n",
57 | ]
58 |
59 | # Check that the diffs match what we expect
60 | self.assertEqual(diffs, expected_diffs)
61 |
62 |
63 | class TestCodingAgent1(unittest.TestCase):
64 | def setUp(self):
65 | # Mock database connection setup
66 | self.mock_db_connection = MagicMock()
67 | self.memory_manager = MemoryManager(db_connection=self.mock_db_connection)
68 | self.codebase = MyCodebase(
69 | db_connection=self.mock_db_connection,
70 | file_extensions=FILE_EXTENSIONS,
71 | ignore_dirs=IGNORE_DIRS,
72 | )
73 |
74 | # Initialize the agent for testing
75 | self.agent = CodingAgent(
76 | memory_manager=self.memory_manager,
77 | codebase=self.codebase,
78 | function_map=[_OP_LIST],
79 | )
80 |
81 | def test_process_json(self):
82 | result = self.agent.process_json('{"key": "value"}')
83 | self.assertEqual(result, {"key": "value"})
84 |
85 | def test_agent_query(self):
86 | self.agent.query = MagicMock()
87 | self.agent.query.return_value = ["response"]
88 | result = list(self.agent.query("input"))
89 | self.assertEqual(result, ["response"])
90 |
91 |
92 | if __name__ == "__main__":
93 | unittest.main()
94 |
--------------------------------------------------------------------------------
/backend/agent/agent_functions/function_ops.py:
--------------------------------------------------------------------------------
1 | from instructor import OpenAISchema
2 | from pydantic import Field
3 | import json
4 | import uuid
5 |
6 |
7 | class AddFunction(OpenAISchema):
8 | """
9 | Represents a function to be added to a Python file. Do not provide Partial values.
10 | """
11 |
12 | file_name: str = Field(
13 | ..., description="The name of the file to add the function to."
14 | )
15 | function_name: str = Field(..., description="The name of the function.")
16 | args: str = Field(..., description="The arguments of the function.")
17 | body: str = Field(..., description="The body of the function.")
18 | decorator_list: list[str] = Field(
19 | [], description="The list of decorators to be applied to the function."
20 | )
21 | returns: str | None = Field(None, description="The return type of the function.")
22 | id: str = str(uuid.uuid4())
23 |
24 | def to_json(self):
25 | out = dict(
26 | id=self.id,
27 | file_name=self.file_name,
28 | function_name=self.function_name,
29 | args=self.args,
30 | body=self.body,
31 | decorator_list=self.decorator_list,
32 | returns=self.returns,
33 | )
34 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
35 |
36 |
37 | class DeleteFunction(OpenAISchema):
38 | """
39 | Represents a request to delete a function from the agent.
40 | """
41 |
42 | file_name: str = Field(
43 | ..., description="The name of the file containing the function to delete."
44 | )
45 | function_name: str = Field(..., description="The name of the function to delete.")
46 | id: str = str(uuid.uuid4())
47 |
48 | def to_json(self):
49 | out = dict(
50 | id=self.id,
51 | file_name=self.file_name,
52 | function_name=self.function_name,
53 | )
54 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
55 |
56 |
57 | class ModifyFunction(OpenAISchema):
58 | """
59 | A class representing modifications to a function. All new values must be complete and will override the existing values. Do not provide Partial values.
60 | """
61 |
62 | file_name: str = Field(
63 | ..., description="The name of the file containing the function to modify."
64 | )
65 | function_name: str = Field(..., description="The name of the function to modify.")
66 | new_args: str | None = Field(
67 | None, description="The new arguments for the function."
68 | )
69 | new_body: str | None = Field(
70 | None,
71 | description="The new body of the function. This will overwrite the old body. Always include a full body.",
72 | )
73 | new_decorator_list: list[str] | None = Field(
74 | None, description="The new list of decorators for the function."
75 | )
76 | new_returns: str | None = Field(
77 | None, description="The new return type for the function."
78 | )
79 | new_name: str | None = Field(None, description="The new name for the function.")
80 | new_docstring: str | None = Field(
81 | None, description="The new docstring for the function."
82 | )
83 | id: str = str(uuid.uuid4())
84 |
85 | def to_json(self):
86 | out = dict(
87 | id=self.id,
88 | file_name=self.file_name,
89 | function_name=self.function_name,
90 | new_args=self.new_args,
91 | new_body=self.new_body,
92 | new_decorator_list=self.new_decorator_list,
93 | new_returns=self.new_returns,
94 | new_name=self.new_name,
95 | )
96 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
97 |
--------------------------------------------------------------------------------
/backend/agent/agent_functions/import_ops.py:
--------------------------------------------------------------------------------
1 | from instructor import OpenAISchema
2 | from pydantic import Field
3 | import json
4 | import uuid
5 |
6 |
7 | class AddImport(OpenAISchema):
8 | """
9 | Represents an import statement to be added to a Python file.
10 | """
11 |
12 | file_name: str = Field(
13 | ..., description="The name of the file to add the import to."
14 | )
15 | module: str = Field(..., description="The name of the module to import.")
16 | names: list | None = Field(
17 | None, description="The names to import from the module. Defaults to None."
18 | )
19 | asnames: list | None = Field(
20 | None, description="The names to import from the module with an alias."
21 | )
22 | objects: list | None = Field(
23 | None, description="The objects to import from the module."
24 | )
25 | id: str = str(uuid.uuid4())
26 |
27 | def to_json(self):
28 | out = dict(
29 | id=self.id,
30 | file_name=self.file_name,
31 | module=self.module,
32 | names=self.names,
33 | asnames=self.asnames,
34 | objects=self.objects,
35 | )
36 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
37 |
38 |
39 | class DeleteImport(OpenAISchema):
40 | """
41 | Represents a request to delete one or more imports from a Python module.
42 | """
43 |
44 | file_name: str = Field(
45 | ..., description="The name of the file to delete the import from."
46 | )
47 | module: str = Field(
48 | ..., description="The name of the module to delete imports from."
49 | )
50 | names: list | None = Field(
51 | None, description="The names to delete from the module. Defaults to None."
52 | )
53 | asnames: list | None = Field(
54 | None, description="The names to delete from the module with an alias."
55 | )
56 | objects: list | None = Field(
57 | None, description="The objects to delete from the module."
58 | )
59 | id: str = str(uuid.uuid4())
60 |
61 | def to_json(self):
62 | out = dict(
63 | id=self.id,
64 | file_name=self.file_name,
65 | module=self.module,
66 | names=self.names,
67 | asnames=self.asnames,
68 | objects=self.objects,
69 | )
70 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
71 |
72 |
73 | class ModifyImport(OpenAISchema):
74 | """
75 | Represents a modification to an import statement in a Python file."""
76 |
77 | file_name: str = Field(
78 | ..., description="The name of the file containing the import to modify."
79 | )
80 | module: str = Field(..., description="The name of the module to modify.")
81 | new_names: list | None = Field(
82 | None, description="The new names to import from the module."
83 | )
84 | new_asnames: list | None = Field(
85 | None, description="The new names to import from the module with an alias."
86 | )
87 | objects_to_remove: list | None = Field(
88 | None, description="The old objects to remove."
89 | )
90 | objects_to_add: list | None = Field(None, description="The new objects to add.")
91 | id: str = str(uuid.uuid4())
92 |
93 | def to_json(self):
94 | out = dict(
95 | id=self.id,
96 | file_name=self.file_name,
97 | module=self.module,
98 | new_names=self.new_names,
99 | new_asnames=self.new_asnames,
100 | new_objects=self.new_objects,
101 | )
102 | return "\n\n```json\n" + json.dumps(out, indent=4) + "\n```\n"
103 |
--------------------------------------------------------------------------------
/frontend/components/ModelSelector.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, use } from 'react';
2 | import { GiLightningBranches, GiStarsStack } from 'react-icons/gi';
3 | import { FaAmazon } from 'react-icons/fa';
4 |
5 | const ModelSelector = () => {
6 | const [activeButton, setActiveButton] = useState('anthropic');
7 | const [modelStatus, setModelStatus] = useState('loading'); // ['loading', 'ready', 'error']
8 |
9 | const fetchCurrentModel = async () => {
10 | try {
11 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/get_model`);
12 | if (!response.ok) {
13 | throw new Error('Failed to fetch current model');
14 | }
15 | const data = await response.json();
16 | setActiveButton(data.model); // Assuming response contains the model name
17 | setModelStatus('ready');
18 | console.log("Model Set: ", data.model);
19 | } catch (error) {
20 | console.error('Failed to fetch current model:', error);
21 | // Optionally, set a different model status here to reflect the error
22 | setModelStatus('error');
23 | }
24 | };
25 |
26 |
27 | const handleButtonClick = (e) => {
28 | console.log(e);
29 | setActiveButton(e);
30 | fetch('http://localhost:8000/set_model', {
31 | method: 'POST',
32 | headers: {
33 | 'Content-Type': 'application/json'
34 | },
35 | body: JSON.stringify({ model: e })
36 | }).then(response => {
37 | if (response.status === 200) {
38 | console.log('success');
39 | } else {
40 | // handle non-200 responses here
41 | console.log(`Response Error with Code: ${response.status}`)
42 | }
43 | }
44 | ).catch(error => {
45 | // handle request errors here
46 | console.error(error);
47 | }
48 | );
49 | };
50 |
51 | const Button = ({ id, icon, text }) => {
52 | return (
53 |
64 | )
65 | };
66 |
67 | useEffect(() => {
68 | if (modelStatus !== 'ready') {
69 | const interval = setInterval(() => {
70 | // Need to keep trying until the backend has loaded the model, then stop
71 | }, 1000);
72 | fetchCurrentModel();
73 | // Clear interval when component unmounts or if modelStatus changes to 'ready'
74 | return () => clearInterval(interval);
75 | }
76 | if (modelStatus === 'ready') {
77 | console.log('Clean Interval');
78 | // return clearInterval(interval);
79 |
80 | }
81 | }, [modelStatus]);
82 |
83 | return (
84 |
{/* You can adjust the color (bg-gray-800), padding (p-2), and roundness (rounded-lg) as needed */}
85 | } text="GPT-3.5" />
86 | < Button id="gpt-4-turbo" icon={< GiStarsStack className='text-purple-500 mx-2' />} text="GPT-4" />
87 | < Button id="anthropic" icon={< FaAmazon className='text-yellow-500 mx-2' />} text="Anth" />
88 |
89 | );
90 | };
91 |
92 | export default ModelSelector;
93 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.db
6 | *.db-journal
7 | # C extensions
8 | *.so
9 | *.cfg
10 | /backend/bin/*
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | share/python-wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 | *lock.json
32 | # *.ipynb
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | *.py,cover
54 | .hypothesis/
55 | .pytest_cache/
56 | cover/
57 |
58 | # Translations
59 | *.mo
60 | *.pot
61 |
62 | # Django stuff:
63 | *.log
64 | local_settings.py
65 | db.sqlite3
66 | db.sqlite3-journal
67 |
68 | # Flask stuff:
69 | instance/
70 | .webassets-cache
71 |
72 | # Scrapy stuff:
73 | .scrapy
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 |
78 | # PyBuilder
79 | .pybuilder/
80 | target/
81 |
82 | # Jupyter Notebook
83 | .ipynb_checkpoints
84 |
85 | # IPython
86 | profile_default/
87 | ipython_config.py
88 |
89 | # pyenv
90 | # For a library or package, you might want to ignore these files since the code is
91 | # intended to run in multiple environments; otherwise, check them in:
92 | # .python-version
93 |
94 | # pipenv
95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
98 | # install all needed dependencies.
99 | #Pipfile.lock
100 |
101 | # poetry
102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
103 | # This is especially recommended for binary packages to ensure reproducibility, and is more
104 | # commonly ignored for libraries.
105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
106 | #poetry.lock
107 |
108 | # pdm
109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
110 | #pdm.lock
111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
112 | # in version control.
113 | # https://pdm.fming.dev/#use-with-ide
114 | .pdm.toml
115 |
116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117 | __pypackages__/
118 |
119 | # Celery stuff
120 | celerybeat-schedule
121 | celerybeat.pid
122 |
123 | # SageMath parsed files
124 | *.sage.py
125 |
126 | # Environments
127 | .env
128 | .venv
129 | env/
130 | venv/
131 | ENV/
132 | env.bak/
133 | venv.bak/
134 |
135 | # Spyder project settings
136 | .spyderproject
137 | .spyproject
138 |
139 | # Rope project settings
140 | .ropeproject
141 |
142 | # mkdocs documentation
143 | /site
144 |
145 | # mypy
146 | .mypy_cache/
147 | .dmypy.json
148 | dmypy.json
149 |
150 | # Pyre type checker
151 | .pyre/
152 |
153 | # pytype static type analyzer
154 | .pytype/
155 |
156 | # Cython debug symbols
157 | cython_debug/
158 |
159 | # PyCharm
160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162 | # and can be added to the global gitignore or merged into this file. For a more nuclear
163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
164 | #.idea/
165 |
--------------------------------------------------------------------------------
/frontend/components/ChatInput.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { AiOutlineSend, AiOutlineCloseCircle } from 'react-icons/ai';
3 | import { Hint } from 'react-autocomplete-hint';
4 | import PropTypes from 'prop-types';
5 |
6 | const ChatInput = ({ onSubmit }) => {
7 | const [input, setInput] = useState('');
8 | const commands = ['/CommandPlan', '/Changes'];
9 | const [file, setFile] = useState(null);
10 | const [previewUrl, setPreviewUrl] = useState(null);
11 |
12 | const handlePaste = (event) => {
13 | event.preventDefault();
14 | const text = event.clipboardData.getData('text/plain');
15 | if (text) {
16 | setInput(input + text);
17 | return;
18 | }
19 | const file = event.clipboardData.files[0];
20 | if (file && file.type.startsWith('image')) {
21 | const reader = new FileReader();
22 | reader.onloadend = () => {
23 | const base64String = reader.result.split(',')[1];
24 | setFile(base64String);
25 | setPreviewUrl(reader.result);
26 | };
27 | reader.readAsDataURL(file);
28 | } else {
29 | alert('Unsupported file type');
30 | }
31 | };
32 |
33 | const handleRemoveImage = () => {
34 | setFile(null);
35 | setPreviewUrl(null);
36 | };
37 |
38 | const handleSubmit = (e) => {
39 | e.preventDefault();
40 | console.log(file);
41 | if (input.trim() || file) {
42 | onSubmit(input, null, file); // Handle the submission logic here
43 | setInput('');
44 | setFile(null);
45 | setPreviewUrl(null);
46 | }
47 | };
48 |
49 | return (
50 |
51 |
90 |
91 | );
92 | };
93 |
94 | // Define prop types for ChatInput
95 | ChatInput.propTypes = {
96 | onSubmit: PropTypes.func.isRequired // Define the prop type and mark it as required
97 | };
98 |
99 | export default ChatInput;
--------------------------------------------------------------------------------
/backend/agent/agent_functions/method_ops.py:
--------------------------------------------------------------------------------
1 | from instructor import OpenAISchema
2 | from pydantic import Field
3 | import json
4 | import uuid
5 |
6 |
7 | class AddMethod(OpenAISchema):
8 | """
9 | Represents a method to be added to a class.
10 | """
11 |
12 | file_name: str = Field(
13 | ...,
14 | description="The name of the file containing the class to add the method to.",
15 | )
16 | class_name: str = Field(
17 | ..., description="The name of the class to add the method to."
18 | )
19 | method_name: str = Field(..., description="The name of the method.")
20 | args: str = Field(..., description="The arguments of the method.")
21 | body: str = Field(..., description="The body of the method.")
22 | decorator_list: list[str] = Field(
23 | [], description="The list of decorators to be applied to the method."
24 | )
25 | returns: str | None = Field(None, description="The return type of the method.")
26 | id: str = str(uuid.uuid4())
27 |
28 | def to_json(self):
29 | out = dict(
30 | id=self.id,
31 | file_name=self.file_name,
32 | class_name=self.class_name,
33 | method_name=self.method_name,
34 | args=self.args,
35 | body=self.body,
36 | decorator_list=self.decorator_list,
37 | returns=self.returns,
38 | )
39 | return "\n\n```json\n" + json.dumps(out) + "\n```\n"
40 |
41 |
42 | class DeleteMethod(OpenAISchema):
43 | """Represents a method to be deleted from a class."""
44 |
45 | file_name: str = Field(
46 | ...,
47 | description="The name of the file containing the class to delete the method from.",
48 | )
49 |
50 | class_name: str = Field(
51 | ..., description="The name of the class to delete the method from."
52 | )
53 | method_name: str = Field(..., description="The name of the method to delete.")
54 | id: str = str(uuid.uuid4())
55 |
56 | def to_string(self):
57 | out = dict(
58 | id=self.id,
59 | file_name=self.file_name,
60 | class_name=self.class_name,
61 | method_name=self.method_name,
62 | )
63 | return "\n\n```json\n" + json.dumps(out) + "\n```\n"
64 |
65 |
66 | class ModifyMethod(OpenAISchema):
67 | """
68 | Represents a method modification operation. Modifications will override the existing method, do not provide Partial values.
69 | """
70 |
71 | file_name: str = Field(
72 | ...,
73 | description="The name of the file containing the class to modify the method in.",
74 | )
75 | class_name: str = Field(
76 | ..., description="The name of the class to modify the method in."
77 | )
78 | method_name: str = Field(..., description="The name of the method to modify.")
79 | new_args: str | None = Field(None, description="The new arguments for the method.")
80 | new_body: str | None = Field(
81 | None,
82 | description="The new body of the method as a string. This will replace the entire existing body of the method.",
83 | )
84 | new_decorator_list: list[str] | None = Field(
85 | None, description="The new list of decorators for the method."
86 | )
87 | new_method_name: str | None = Field(
88 | None, description="The new name for the method."
89 | )
90 | new_returns: str | None = Field(
91 | None, description="The new return type for the method."
92 | )
93 | new_docstring: str | None = Field(
94 | None, description="The new docstring for the function."
95 | )
96 | id: str = str(uuid.uuid4())
97 |
98 | def to_json(self):
99 | out = dict(
100 | id=self.id,
101 | file_name=self.file_name,
102 | class_name=self.class_name,
103 | method_name=self.method_name,
104 | new_args=self.new_args,
105 | new_body=self.new_body,
106 | new_decorator_list=self.new_decorator_list,
107 | new_method_name=self.new_method_name,
108 | new_returns=self.new_returns,
109 | )
110 | return "\n\n```json\n" + json.dumps(out) + "\n```\n"
111 |
--------------------------------------------------------------------------------
/frontend/components/ChatBox.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect } from 'react';
2 | import ReactMarkdown from 'react-markdown';
3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4 | import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5 | import ReactTooltip from 'react-tooltip';
6 | import { Virtuoso } from 'react-virtuoso';
7 | import { CopyToClipboard } from 'react-copy-to-clipboard';
8 | import { FaClipboardCheck } from 'react-icons/fa';
9 |
10 | const CodeBlock = React.memo(({ node, inline, className, children }) => {
11 | const match = /language-(\w+)/.exec(className || '');
12 | const lang = match && match[1] ? match[1] : '';
13 | return !inline && match ? (
14 |
84 | )}
85 | {/* Add other details as necessary */}
86 |
87 |
{/* This is your footer area where the buttons are */}
88 |
102 |
109 |
110 | {error &&
{error}
}
111 |
112 |
113 | );
114 | };
115 |
116 | export default OperationCard;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # GPT-CodeKnot 🚀
3 |
4 |    [](https://github.com/blazickjp/GPT-CodeApp/actions/workflows/pytest_ubuntu.yml)  
5 |
6 | Embark on a coding adventure with GPT-CodeApp, your new AI-powered coding companion! 🎉 This isn't just another Chat-GPT clone; it's your gateway to a smoother coding experience, packed with features you've always wished for.
7 |
8 |
9 | 
10 |
11 | ## 📚 Table of Contents
12 |
13 | - [Installation and Setup](#installation)
14 | - [Usage](#usage)
15 | - [Contributing](#contributing)
16 | - [Tests](#tests)
17 | - [License](#license)
18 | - [Questions](#questions)
19 | - [Features](#features)
20 |
21 | ## 🛠️ Installation and Setup
22 |
23 | Setting up GPT-CodeApp is as easy as 1, 2, 3! Our application consists of a frontend and a backend, both of which need to be set up separately.
24 |
25 | Jump straight into action with these simple setup steps for both the frontend and backend. Let's get the engines running!
26 |
27 | ### Backend: The Brain 🧠
28 |
29 | 1. **Enter the Backend Lair:**
30 | ```bash
31 | cd backend
32 | ```
33 | 2. **Summon the Python Dependencies:**
34 | ```bash
35 | python3 -m venv env
36 | source env/bin/activate
37 | pip install -r requirements.txt
38 | ```
39 | 3. **Awaken the Backend Beast:**
40 | ```bash
41 | uvicorn main:app --reload
42 | ```
43 |
44 | ### Frontend: The Face 😎
45 |
46 | 1. **Dive into the Frontend Fortress:**
47 | ```bash
48 | cd frontend
49 | ```
50 | 2. **Gather the JavaScript Warriors:**
51 | ```bash
52 | npm install
53 | ```
54 | 3. **Launch the Visual Vanguard:**
55 | ```bash
56 | npm run dev
57 | ```
58 |
59 | ## 🗝️ Model Authentication
60 |
61 | Before you can use the GPT-CodeApp, you'll need to authenticate with the AI models from Bedrock and OpenAI. Here's how to do it:
62 |
63 | ### Bedrock Authentication
64 |
65 | Bedrock uses standard AWS authentication for boto3 and access to the ClaudEv2 models. Follow these steps to set it up:
66 |
67 | 1. Install the AWS CLI on your machine. You can do this by following the instructions in the [official AWS CLI User Guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
68 |
69 | 2. Configure your AWS credentials by running the following command and providing your AWS Access Key ID and Secret Access Key when prompted:
70 |
71 | ```bash
72 | aws configure
73 | ```
74 |
75 | 3. Set the `AWS_PROFILE` environment variable to the name of the AWS profile you want to use:
76 |
77 | ```bash
78 | export AWS_PROFILE=your-profile-name
79 | ```
80 |
81 | ### OpenAI Authentication
82 |
83 | OpenAI uses environment variables for authentication. Follow these steps to set it up:
84 |
85 | 1. Get your OpenAI API key. You can find this in the [OpenAI Dashboard](https://beta.openai.com/dashboard/).
86 |
87 | 2. Set the `OPENAI_API_KEY` environment variable to your OpenAI API key:
88 |
89 | ```bash
90 | export OPENAI_API_KEY=your-api-key
91 | ```
92 |
93 | Now you're ready to start using the GPT-CodeApp with Anthropic and OpenAI models!
94 |
95 | ## 🎮 Usage
96 | Dive into the GPT-CodeApp experience with these simple steps:
97 |
98 | - **Step 1:** Launch your browser and head over to `http://localhost:3000` to greet your new AI coding companion.
99 |
100 | - **Step 3:** Ensure you've setup authentication with either OpenAI (API KEY) or Bedrock (for Anthropic models). With authentication out of the way, you're ready to roll! Look for the sidebar on the main interface. Here, you'll find a spot to input the **full path** to the directory of your project. This is crucial for GPT-CodeApp to understand the context of your work and provide tailored assistance.
101 | - **Step 4:** Now, it's time to ask away! Load *focus* files into the search bar at the top. Make sure to send them to the backend with the send button. Type your coding queries or dilemmas into the text box and hit submit. GPT-CodeApp will churn through its AI brain to bring you crisp, accurate coding advice or solutions.
102 | - **Step 5:** Explore the responses, refine your questions for deeper insights, or kick off a new query. The AI is here to assist you through thick and thin code.
103 | - **Step 6:** Base prompts can be found in the backend/agent/agent_prompts.py file and set to load in the app_setup.py. You can also add your own prompts to the file anytime.
104 | - **Notes:** The input text box accepts images and text. To add an image to the prompt simply copy the image and paste it into the text box.
105 |
106 | ## 🤝 Contributing
107 |
108 | Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
109 |
110 | See [Contributing Guide](CONTRIBUTING.md)
111 |
112 | ## 🧪 Tests
113 |
114 |
--------------------------------------------------------------------------------
/frontend/components/LeftSidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import PropTypes from 'prop-types'; // Import PropTypes
3 | import { FaTrash } from 'react-icons/fa';
4 | import { HiOutlineCheckCircle } from 'react-icons/hi';
5 | import DirectorySelectOption from './DirectorySelectOption';
6 |
7 | const LeftSidebar = ({ isLeftSidebarOpen }) => {
8 | const [prompts, setPrompts] = useState([]);
9 | const [sidebarKey] = useState(0);
10 | const [saving, setSaving] = useState({}); // Track saving status by prompt ID
11 |
12 | const fetchPrompts = async () => {
13 | try {
14 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/list_prompts`);
15 | if (!response.ok) {
16 | throw new Error('Error fetching prompts');
17 | }
18 | const data = await response.json();
19 | setPrompts(data.prompts);
20 | console.log(data.prompts);
21 | } catch (error) {
22 | console.error('Error fetching prompts', error);
23 | }
24 | };
25 |
26 | const handleDeletePrompt = async (id) => {
27 | console.log(prompts);
28 | console.log("Deleting prompt: ", id);
29 | try {
30 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/delete_prompt`, {
31 | method: 'POST',
32 | headers: {
33 | 'Content-Type': 'application/json',
34 | },
35 | body: JSON.stringify({ "prompt_id": id }),
36 | });
37 | if (!response.ok) {
38 | throw new Error('Error deleting prompt');
39 | }
40 | await fetchPrompts();
41 | } catch (error) {
42 | console.error('Error deleting prompt', error);
43 | }
44 | };
45 |
46 | const setPrompt = async (id, prompt) => {
47 | // Set the saving state to 'pending' for the specific prompt ID
48 | setSaving(prev => ({ ...prev, [id]: 'pending' }));
49 | console.log("Setting prompt: ", id);
50 |
51 | try {
52 | const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/save_prompt`, {
53 | method: 'POST',
54 | headers: {
55 | 'Content-Type': 'application/json',
56 | },
57 | body: JSON.stringify({ "prompt_name": id, "prompt": prompt }),
58 | });
59 | if (!response.ok) {
60 | throw new Error('Error setting prompt');
61 | }
62 |
63 | // After success, update only the status of the specific prompt ID
64 | setTimeout(() => {
65 | setSaving(prev => ({ ...prev, [id]: 'success' }));
66 | setTimeout(() => setSaving(prev => ({ ...prev, [id]: false })), 2000);
67 | }, 1000);
68 |
69 | fetchPrompts();
70 |
71 |
72 | } catch (error) {
73 | console.error('Error setting prompt', error);
74 | setTimeout(() => {
75 | setSaving(prev => ({ ...prev, [id]: 'error' }));
76 | setTimeout(() => setSaving(prev => ({ ...prev, [id]: false })), 2000);
77 | }, 1000);
78 | }
79 | };
80 |
81 | useEffect(() => {
82 | fetchPrompts();
83 | }, [sidebarKey, isLeftSidebarOpen]);
84 |
85 | return (
86 |