From 11752ba26d790cbc9a3b0b343e7ab33287cc733f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 2 Jun 2023 23:21:42 -0400 Subject: recipes folder --- continuedev/src/continuedev/core/autopilot.py | 2 +- continuedev/src/continuedev/core/policy.py | 16 +- continuedev/src/continuedev/core/sdk.py | 2 +- continuedev/src/continuedev/libs/steps/__init__.py | 1 - continuedev/src/continuedev/libs/steps/chroma.py | 61 ---- .../src/continuedev/libs/steps/continue_step.py | 37 --- .../src/continuedev/libs/steps/core/core.py | 208 ------------ .../libs/steps/draft/abstract_method.py | 19 -- .../src/continuedev/libs/steps/draft/dlt.py | 114 ------- .../src/continuedev/libs/steps/draft/redux.py | 47 --- .../src/continuedev/libs/steps/draft/typeorm.py | 43 --- continuedev/src/continuedev/libs/steps/main.py | 358 --------------------- .../src/continuedev/libs/steps/migration.py | 26 -- continuedev/src/continuedev/libs/steps/nate.py | 214 ------------ continuedev/src/continuedev/libs/steps/pytest.py | 37 --- .../src/continuedev/libs/steps/react_posthog.py | 0 .../src/continuedev/libs/steps/steps_on_startup.py | 30 -- continuedev/src/continuedev/libs/steps/ty.py | 155 --------- .../recipes/ContinueRecipeRecipe/README.md | 7 + .../recipes/ContinueRecipeRecipe/main.py | 37 +++ .../recipes/CreatePipelineRecipe/README.md | 0 .../recipes/CreatePipelineRecipe/main.py | 33 ++ .../recipes/CreatePipelineRecipe/steps.py | 85 +++++ continuedev/src/continuedev/recipes/README.md | 9 + .../continuedev/recipes/TemplateRecipe/README.md | 7 + .../src/continuedev/recipes/TemplateRecipe/main.py | 27 ++ .../recipes/WritePytestsRecipe/README.md | 7 + .../continuedev/recipes/WritePytestsRecipe/main.py | 37 +++ .../src/continuedev/server/session_manager.py | 6 - continuedev/src/continuedev/steps/__init__.py | 1 + continuedev/src/continuedev/steps/chroma.py | 61 ++++ continuedev/src/continuedev/steps/core/core.py | 208 ++++++++++++ .../src/continuedev/steps/draft/abstract_method.py | 19 ++ .../src/continuedev/steps/draft/migration.py | 25 ++ continuedev/src/continuedev/steps/draft/redux.py | 47 +++ continuedev/src/continuedev/steps/draft/typeorm.py | 43 +++ continuedev/src/continuedev/steps/main.py | 358 +++++++++++++++++++++ .../src/continuedev/steps/steps_on_startup.py | 28 ++ 38 files changed, 1048 insertions(+), 1367 deletions(-) delete mode 100644 continuedev/src/continuedev/libs/steps/__init__.py delete mode 100644 continuedev/src/continuedev/libs/steps/chroma.py delete mode 100644 continuedev/src/continuedev/libs/steps/continue_step.py delete mode 100644 continuedev/src/continuedev/libs/steps/core/core.py delete mode 100644 continuedev/src/continuedev/libs/steps/draft/abstract_method.py delete mode 100644 continuedev/src/continuedev/libs/steps/draft/dlt.py delete mode 100644 continuedev/src/continuedev/libs/steps/draft/redux.py delete mode 100644 continuedev/src/continuedev/libs/steps/draft/typeorm.py delete mode 100644 continuedev/src/continuedev/libs/steps/main.py delete mode 100644 continuedev/src/continuedev/libs/steps/migration.py delete mode 100644 continuedev/src/continuedev/libs/steps/nate.py delete mode 100644 continuedev/src/continuedev/libs/steps/pytest.py delete mode 100644 continuedev/src/continuedev/libs/steps/react_posthog.py delete mode 100644 continuedev/src/continuedev/libs/steps/steps_on_startup.py delete mode 100644 continuedev/src/continuedev/libs/steps/ty.py create mode 100644 continuedev/src/continuedev/recipes/ContinueRecipeRecipe/README.md create mode 100644 continuedev/src/continuedev/recipes/ContinueRecipeRecipe/main.py create mode 100644 continuedev/src/continuedev/recipes/CreatePipelineRecipe/README.md create mode 100644 continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py create mode 100644 continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py create mode 100644 continuedev/src/continuedev/recipes/README.md create mode 100644 continuedev/src/continuedev/recipes/TemplateRecipe/README.md create mode 100644 continuedev/src/continuedev/recipes/TemplateRecipe/main.py create mode 100644 continuedev/src/continuedev/recipes/WritePytestsRecipe/README.md create mode 100644 continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py create mode 100644 continuedev/src/continuedev/steps/__init__.py create mode 100644 continuedev/src/continuedev/steps/chroma.py create mode 100644 continuedev/src/continuedev/steps/core/core.py create mode 100644 continuedev/src/continuedev/steps/draft/abstract_method.py create mode 100644 continuedev/src/continuedev/steps/draft/migration.py create mode 100644 continuedev/src/continuedev/steps/draft/redux.py create mode 100644 continuedev/src/continuedev/steps/draft/typeorm.py create mode 100644 continuedev/src/continuedev/steps/main.py create mode 100644 continuedev/src/continuedev/steps/steps_on_startup.py (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 6e920ab4..85f65dc3 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -8,7 +8,7 @@ from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue from ..models.main import ContinueBaseModel from .main import Policy, History, FullState, Step, HistoryNode -from ..libs.steps.core.core import ReversibleStep, ManualEditStep, UserInputStep +from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK import asyncio diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 6e264bab..f7fcf21a 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -1,13 +1,13 @@ from typing import List, Tuple, Type -from ..libs.steps.steps_on_startup import StepsOnStartupStep -from ..libs.steps.draft.dlt import CreatePipelineStep +from ..steps.steps_on_startup import StepsOnStartupStep +from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe from .main import Step, Validator, History, Policy from .observation import Observation, TracebackObservation, UserInputObservation -from ..libs.steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep, MessageStep, EmptyStep -from ..libs.steps.nate import WritePytestsStep, CreateTableStep +from ..steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep, MessageStep, EmptyStep +from ..recipes.WritePytestsRecipe.main import WritePytestsRecipe # from ..libs.steps.chroma import AnswerQuestionChroma, EditFileChroma -from ..libs.steps.continue_step import ContinueStepStep +from ..recipes.ContinueRecipeRecipe.main import ContinueStepStep class DemoPolicy(Policy): @@ -22,11 +22,9 @@ class DemoPolicy(Policy): if observation is not None and isinstance(observation, UserInputObservation): # This could be defined with ObservationTypePolicy. Ergonomics not right though. if " test" in observation.user_input.lower(): - return WritePytestsStep(instructions=observation.user_input) + return WritePytestsRecipe(instructions=observation.user_input) elif "/dlt" in observation.user_input.lower() or " dlt" in observation.user_input.lower(): - return CreatePipelineStep() - elif "/table" in observation.user_input: - return CreateTableStep(sql_str=" ".join(observation.user_input.split(" ")[1:])) + return CreatePipelineRecipe() # elif "/ask" in observation.user_input: # return AnswerQuestionChroma(question=" ".join(observation.user_input.split(" ")[1:])) # elif "/edit" in observation.user_input: diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index af7754cc..5d0f03fe 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -10,7 +10,7 @@ from ..libs.llm.openai import OpenAI from .observation import Observation from ..server.ide_protocol import AbstractIdeProtocolServer from .main import History, Step -from ..libs.steps.core.core import * +from ..steps.core.core import * from .env import get_env_var, make_sure_env_exists diff --git a/continuedev/src/continuedev/libs/steps/__init__.py b/continuedev/src/continuedev/libs/steps/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/continuedev/src/continuedev/libs/steps/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/continuedev/src/continuedev/libs/steps/chroma.py b/continuedev/src/continuedev/libs/steps/chroma.py deleted file mode 100644 index 39424c5c..00000000 --- a/continuedev/src/continuedev/libs/steps/chroma.py +++ /dev/null @@ -1,61 +0,0 @@ -from textwrap import dedent -from typing import Coroutine, Union -from ...core.observation import Observation, TextObservation -from ...core.main import Step, ContinueSDK -from .core.core import EditFileStep -from ..chroma.query import query_codebase_index -from .core.core import EditFileStep - - -class AnswerQuestionChroma(Step): - question: str - _answer: Union[str, None] = None - name: str = "Answer Question" - - async def describe(self, llm) -> Coroutine[str, None, None]: - if self._answer is None: - return f"Answering the question: {self.question}" - else: - return self._answer - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - results = query_codebase_index(self.question) - - code_snippets = "" - - files = [] - for node in results.source_nodes: - resource_name = list(node.node.relationships.values())[0] - filepath = resource_name[:resource_name.index("::")] - files.append(filepath) - code_snippets += f"""{filepath}```\n{node.node.text}\n```\n\n""" - - prompt = dedent(f"""Here are a few snippets of code that might be useful in answering the question: - - {code_snippets} - - Here is the question to answer: - - {self.question} - - Here is the answer:""") - - answer = (await sdk.models.gpt35()).complete(prompt) - print(answer) - self._answer = answer - - await sdk.ide.setFileOpen(files[0]) - - -class EditFileChroma(Step): - request: str - hide: bool = True - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - results = query_codebase_index(self.request) - - resource_name = list( - results.source_nodes[0].node.relationships.values())[0] - filepath = resource_name[:resource_name.index("::")] - - await sdk.run_step(EditFileStep(filepath=filepath, prompt=f"Here is the code:\n\n{{code}}\n\nHere is the user request:\n\n{self.request}\n\nHere is the code after making the requested changes:\n")) diff --git a/continuedev/src/continuedev/libs/steps/continue_step.py b/continuedev/src/continuedev/libs/steps/continue_step.py deleted file mode 100644 index 253bb490..00000000 --- a/continuedev/src/continuedev/libs/steps/continue_step.py +++ /dev/null @@ -1,37 +0,0 @@ -from textwrap import dedent -from ...models.filesystem import RangeInFile -from .main import EditHighlightedCodeStep -from ...core.main import Step -from ...core.sdk import ContinueSDK - - -class ContinueStepStep(Step): - name: str = "Write your own Continue Step." - prompt: str - - async def run(self, sdk: ContinueSDK): - await sdk.run_step(EditHighlightedCodeStep(user_input=dedent(f"""\ - Here is an example of a Step that runs a command and then edits a file. - - ```python - from ...core.main import Step - from ...core.sdk import ContinueSDK - - class RunCommandAndEditFileStep(Step): - name: str = "Run a command and then edit a file." - command: str - file_path: str - prompt: str - - async def run(self, sdk: ContinueSDK): - await sdk.run([command]) - await sdk.edit_file(filename=self.file_path, prompt=self.prompt) - ``` - - Please edit the code to write your own Step that does the following: - - {self.prommpt} - - It should be a subclass of Step as above, implementing the `run` method, and using pydantic attributes to define the parameters. - - """))) diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py deleted file mode 100644 index 9a5d54f0..00000000 --- a/continuedev/src/continuedev/libs/steps/core/core.py +++ /dev/null @@ -1,208 +0,0 @@ -# These steps are depended upon by ContinueSDK -import subprocess -from textwrap import dedent -from typing import Coroutine, List, Union -from ...llm.prompt_utils import MarkdownStyleEncoderDecoder - -from ....models.filesystem_edit import EditDiff, FileEditWithFullContents, FileSystemEdit -from ....models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents -from ....core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation -from ....core.main import Step, SequentialStep - - -class ContinueSDK: - pass - - -class Models: - pass - - -class ReversibleStep(Step): - async def reverse(self, sdk: ContinueSDK): - raise NotImplementedError - - -class FileSystemEditStep(ReversibleStep): - edit: FileSystemEdit - _diff: Union[EditDiff, None] = None - - hide: bool = True - - async def run(self, sdk: "ContinueSDK") -> Coroutine[Observation, None, None]: - self._diff = await sdk.ide.applyFileSystemEdit(self.edit) - return None - - async def reverse(self, sdk: "ContinueSDK"): - await sdk.ide.applyFileSystemEdit(self._diff.backward) - # Where and when should file saves happen? - - -class ShellCommandsStep(Step): - cmds: List[str] - cwd: str | None = None - name: str = "Run Shell Commands" - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - cmds_str = "\n".join(self.cmds) - return (await models.gpt35()).complete(f"{cmds_str}\n\nSummarize what was done in these shell commands, using markdown bullet points:") - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd - - process = subprocess.Popen( - '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) - - stdin_input = "\n".join(self.cmds) - out, err = process.communicate(stdin_input.encode()) - - # If it fails, return the error - if err is not None and err != "": - return TextObservation(text=err) - - return None - - -class EditCodeStep(Step): - # Might make an even more specific atomic step, which is "apply file edit" - range_in_files: List[RangeInFile] - prompt: str # String with {code} somewhere - name: str = "Edit code" - - _edit_diffs: Union[List[EditDiff], None] = None - _prompt: Union[str, None] = None - _completion: Union[str, None] = None - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - if self._edit_diffs is None: - return "Editing files: " + ", ".join(map(lambda rif: rif.filepath, self.range_in_files)) - elif len(self._edit_diffs) == 0: - return "No edits made" - else: - return (await models.gpt35()).complete(dedent(f"""{self._prompt}{self._completion} - - Maximally concise summary of changes in bullet points (can use markdown): - """)) - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - rif_with_contents = [] - for range_in_file in self.range_in_files: - file_contents = await sdk.ide.readRangeInFile(range_in_file) - rif_with_contents.append( - RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) - enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) - code_string = enc_dec.encode() - prompt = self.prompt.format(code=code_string) - - completion = (await sdk.models.gpt35()).complete(prompt) - - # Temporarily doing this to generate description. - self._prompt = prompt - self._completion = completion - - file_edits = enc_dec.decode(completion) - - self._edit_diffs = [] - for file_edit in file_edits: - diff = await sdk.apply_filesystem_edit(file_edit) - self._edit_diffs.append(diff) - - for filepath in set([file_edit.filepath for file_edit in file_edits]): - await sdk.ide.saveFile(filepath) - await sdk.ide.setFileOpen(filepath) - - return None - - -class EditFileStep(Step): - filepath: str - prompt: str - hide: bool = True - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Editing file: " + self.filepath - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - file_contents = await sdk.ide.readFile(self.filepath) - await sdk.run_step(EditCodeStep( - range_in_files=[RangeInFile.from_entire_file( - self.filepath, file_contents)], - prompt=self.prompt - )) - - -class ManualEditStep(ReversibleStep): - edit_diff: EditDiff - hide: bool = True - - hide: bool = True - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Manual edit step" - # TODO - only handling FileEdit here, but need all other types of FileSystemEdits - # Also requires the merge_file_edit function - # return llm.complete(dedent(f"""This code was replaced: - - # {self.edit_diff.backward.replacement} - - # With this code: - - # {self.edit_diff.forward.replacement} - - # Maximally concise summary of changes in bullet points (can use markdown): - # """)) - - @classmethod - def from_sequence(cls, edits: List[FileEditWithFullContents]) -> "ManualEditStep": - diffs = [] - for edit in edits: - _, diff = FileSystem.apply_edit_to_str( - edit.fileContents, edit.fileEdit) - diffs.append(diff) - return cls(edit_diff=EditDiff.from_sequence(diffs)) - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return None - - async def reverse(self, sdk: ContinueSDK): - await sdk.ide.applyFileSystemEdit(self.edit_diff.backward) - - -class UserInputStep(Step): - user_input: str - name: str = "User Input" - hide: bool = True - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return self.user_input - - async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: - return UserInputObservation(user_input=self.user_input) - - -class WaitForUserInputStep(Step): - prompt: str - name: str = "Waiting for user input" - - _description: Union[str, None] = None - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return self.prompt - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt - resp = await sdk.wait_for_user_input() - return TextObservation(text=resp) - - -class WaitForUserConfirmationStep(Step): - prompt: str - name: str = "Waiting for user confirmation" - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return self.prompt - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt - resp = await sdk.wait_for_user_input() - return TextObservation(text=resp) diff --git a/continuedev/src/continuedev/libs/steps/draft/abstract_method.py b/continuedev/src/continuedev/libs/steps/draft/abstract_method.py deleted file mode 100644 index f3131c4b..00000000 --- a/continuedev/src/continuedev/libs/steps/draft/abstract_method.py +++ /dev/null @@ -1,19 +0,0 @@ -from ....core.sdk import ContinueSDK -from ....core.main import Step - - -class ImplementAbstractMethodStep(Step): - name: str = "Implement abstract method for all subclasses" - method_name: str - class_name: str - - async def run(self, sdk: ContinueSDK): - - implementations = await sdk.lsp.go_to_implementations(self.class_name) - - for implementation in implementations: - - await sdk.edit_file( - range_in_files=[implementation.range_in_file], - prompt=f"Implement method `{self.method_name}` for this subclass of `{self.class_name}`", - ) diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py deleted file mode 100644 index 9cec5014..00000000 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ /dev/null @@ -1,114 +0,0 @@ -from textwrap import dedent - -from ....core.sdk import Models - -from ....core.observation import DictObservation -from ....models.filesystem_edit import AddFile -from ....core.main import Step -from ....core.sdk import ContinueSDK -from ..core.core import WaitForUserInputStep -from ..main import MessageStep - - -class SetupPipelineStep(Step): - hide: bool = True - name: str = "Setup dlt Pipeline" - - api_description: str # e.g. "I want to load data from the weatherapi.com API" - - async def describe(self, models: Models): - return dedent(f"""\ - This step will create a new dlt pipeline that loads data from an API, as per your request: - {self.api_description} - """) - - async def run(self, sdk: ContinueSDK): - source_name = (await sdk.models.gpt35()).complete( - f"Write a snake_case name for the data source described by {self.api_description}: ").strip() - filename = f'{source_name}.py' - - # running commands to get started when creating a new dlt pipeline - await sdk.run([ - 'python3 -m venv env', - 'source env/bin/activate', - 'pip install dlt', - f'dlt init {source_name} duckdb', - 'Y', - 'pip install -r requirements.txt' - ]) - - # editing the resource function to call the requested API - await sdk.edit_file( - filename=filename, - prompt=f'Edit the resource function to call the API described by this: {self.api_description}' - ) - - # wait for user to put API key in secrets.toml - await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") - await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`") - return DictObservation(values={"source_name": source_name}) - - -class ValidatePipelineStep(Step): - hide: bool = True - - async def run(self, sdk: ContinueSDK): - source_name = sdk.history.last_observation().values["source_name"] - filename = f'{source_name}.py' - - # test that the API call works - await sdk.run(f'python3 {filename}') - - # remove exit() from the main main function - await sdk.edit_file( - filename=filename, - prompt='Remove exit() from the main function' - ) - - # load the data into the DuckDB instance - await sdk.run(f'python3 {filename}') - - table_name = f"{source_name}.{source_name}_resource" - tables_query_code = dedent(f'''\ - import duckdb - - # connect to DuckDB instance - conn = duckdb.connect(database="{source_name}.duckdb") - - # get table names - rows = conn.execute("SELECT * FROM {table_name};").fetchall() - - # print table names - for row in rows: - print(row) - ''') - - query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" - await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code)) - await sdk.run('env/bin/python3 query.py') - - -class CreatePipelineStep(Step): - hide: bool = True - - async def run(self, sdk: ContinueSDK): - await sdk.run_step( - MessageStep(message=dedent("""\ - This recipe will walk you through the process of creating a dlt pipeline for your chosen data source. With the help of Continue, you will: - - Create a Python virtual environment with dlt installed - - Run `dlt init` to generate a pipeline template - - Write the code to call the API - - Add any required API keys to the `secrets.toml` file - - Test that the API call works - - Load the data into a local DuckDB instance - - Write a query to view the data""")) >> - WaitForUserInputStep(prompt="What API do you want to load data from?") >> - SetupPipelineStep(api_description="WeatherAPI.com API") >> - MessageStep(message=dedent("""\ - This step will validate that your dlt pipeline is working as expected: - - Test that the API call works - - Load the data into a local DuckDB instance - - Write a query to view the data - """)) >> - ValidatePipelineStep() - ) diff --git a/continuedev/src/continuedev/libs/steps/draft/redux.py b/continuedev/src/continuedev/libs/steps/draft/redux.py deleted file mode 100644 index efaa9ba4..00000000 --- a/continuedev/src/continuedev/libs/steps/draft/redux.py +++ /dev/null @@ -1,47 +0,0 @@ -from ....core.main import Step -from ....core.sdk import ContinueSDK -from ..core.core import EditFileStep - - -class EditReduxStateStep(Step): - - description: str # e.g. "I want to load data from the weatherapi.com API" - - async def run(self, sdk: ContinueSDK): - # Find the right file to edit - - # RootStore - store_filename = "" - sdk.run_step( - EditFileStep( - filename=store_filename, - prompt=f"Edit the root store to add a new slice for {self.description}" - ) - ) - store_file_contents = await sdk.ide.readFile(store_filename) - - # Selector - selector_filename = "" - sdk.run_step(EditFileStep( - filepath=selector_filename, - prompt=f"Edit the selector to add a new property for {self.description}. The store looks like this: {store_file_contents}" - ) - - # Reducer - reducer_filename = "" - sdk.run_step(EditFileStep( - filepath=reducer_filename, - prompt=f"Edit the reducer to add a new property for {self.description}. The store looks like this: {store_file_contents}" - - """ - Starts with implementing selector - 1. RootStore - 2. Selector - 3. Reducer or entire slice - - Need to first determine whether this is an: - 1. edit - 2. add new reducer and property in existing slice - 3. add whole new slice - 4. build redux from scratch - """ diff --git a/continuedev/src/continuedev/libs/steps/draft/typeorm.py b/continuedev/src/continuedev/libs/steps/draft/typeorm.py deleted file mode 100644 index d06a6fb4..00000000 --- a/continuedev/src/continuedev/libs/steps/draft/typeorm.py +++ /dev/null @@ -1,43 +0,0 @@ -from textwrap import dedent -from ....core.main import Step -from ....core.sdk import ContinueSDK - - -class CreateTableStep(Step): - sql_str: str - name: str = "Create a table in TypeORM" - - async def run(self, sdk: ContinueSDK): - # Write TypeORM entity - entity_name = self.sql_str.split(" ")[2].capitalize() - await sdk.edit_file( - f"src/entity/{entity_name}.ts", - dedent(f"""\ - {self.sql_str} - - Write a TypeORM entity called {entity_name} for this table, importing as necessary:""") - ) - - # Add entity to data-source.ts - await sdk.edit_file(filepath="src/data-source.ts", prompt=f"Add the {entity_name} entity:") - - # Generate blank migration for the entity - out = await sdk.run(f"npx typeorm migration:create ./src/migration/Create{entity_name}Table") - migration_filepath = out.text.split(" ")[1] - - # Wait for user input - await sdk.wait_for_user_confirmation("Fill in the migration?") - - # Fill in the migration - await sdk.edit_file( - migration_filepath, - dedent(f"""\ - This is the table that was created: - - {self.sql_str} - - Fill in the migration for the table:"""), - ) - - # Run the migration - await sdk.run("npx typeorm-ts-node-commonjs migration:run -d ./src/data-source.ts") diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py deleted file mode 100644 index 73ad3352..00000000 --- a/continuedev/src/continuedev/libs/steps/main.py +++ /dev/null @@ -1,358 +0,0 @@ -from typing import Coroutine, List, Union - -from pydantic import BaseModel - -from ..util.traceback_parsers import parse_python_traceback -from ..llm import LLM -from ...models.main import Traceback, Range -from ...models.filesystem_edit import EditDiff, FileEdit -from ...models.filesystem import RangeInFile, RangeInFileWithContents -from ...core.observation import Observation, TextObservation, TracebackObservation -from ..llm.prompt_utils import MarkdownStyleEncoderDecoder -from textwrap import dedent -from ...core.main import Step -from ...core.sdk import ContinueSDK, Models -from ...core.observation import Observation -import subprocess -from .core.core import EditCodeStep - - -class RunCodeStep(Step): - cmd: str - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return f"Ran command: `{self.cmd}`" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - result = subprocess.run( - self.cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout = result.stdout.decode("utf-8") - stderr = result.stderr.decode("utf-8") - print(stdout, stderr) - - # If it fails, return the error - tb = parse_python_traceback(stdout) or parse_python_traceback(stderr) - if tb: - return TracebackObservation(traceback=tb) - else: - self.hide = True - return None - - -class Policy(BaseModel): - pass - - -class RunPolicyUntilDoneStep(Step): - policy: "Policy" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - next_step = self.policy.next(sdk.history) - while next_step is not None: - observation = await sdk.run_step(next_step) - next_step = self.policy.next(sdk.history) - return observation - - -class RunCommandStep(Step): - cmd: str - name: str = "Run command" - _description: str = None - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - if self._description is not None: - return self._description - return self.cmd - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - cwd = await sdk.ide.getWorkspaceDirectory() - result = subprocess.run( - self.cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) - stdout = result.stdout.decode("utf-8") - stderr = result.stderr.decode("utf-8") - print(stdout, stderr) - - # If it fails, return the error - if result.returncode != 0: - return TextObservation(text=stderr) - else: - return TextObservation(text=stdout) - - -class FasterEditHighlightedCodeStep(Step): - user_input: str - hide = True - _completion: str = "Edit Code" - _edit_diffs: Union[List[EditDiff], None] = None - _prompt: str = dedent("""\ - You will be given code to edit in order to perfectly satisfy the user request. All the changes you make must be described as replacements, which you should format in the following way: - FILEPATH - - REPLACE_ME - - REPLACE_WITH - - - where and can be multiple lines, but should be the mininum needed to make the edit. Be sure to maintain existing whitespace at the start of lines. - - For example, if you want to replace the code `x = 1` with `x = 2` in main.py, you would write: - FILEPATH - main.py - REPLACE_ME - x = 1 - REPLACE_WITH - x = 2 - If you wanted to delete the code - ``` - def sum(a, b): - return a + b - ``` - in main.py, you would write: - FILEPATH - main.py - REPLACE_ME - def sum(a, b): - return a + b - REPLACE_WITH - - You may need to make multiple edits; respond with exactly as many as needed. - - Below is the code before changes: - - {code} - - This is the user request: "{user_input}" - Here is the description of changes to make: -""") - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Editing highlighted code" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - range_in_files = await sdk.ide.getHighlightedCode() - if len(range_in_files) == 0: - # Get the full contents of all open files - files = await sdk.ide.getOpenFiles() - contents = {} - for file in files: - contents[file] = await sdk.ide.readFile(file) - - range_in_files = [RangeInFile.from_entire_file( - filepath, content) for filepath, content in contents.items()] - - rif_with_contents = [] - for range_in_file in range_in_files: - file_contents = await sdk.ide.readRangeInFile(range_in_file) - rif_with_contents.append( - RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) - enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) - code_string = enc_dec.encode() - prompt = self._prompt.format( - code=code_string, user_input=self.user_input) - - rif_dict = {} - for rif in rif_with_contents: - rif_dict[rif.filepath] = rif.contents - - completion = (await sdk.models.gpt35()).complete(prompt) - - # Temporarily doing this to generate description. - self._prompt = prompt - self._completion = completion - print(completion) - - # ALTERNATIVE DECODING STEP HERE - raw_file_edits = [] - lines = completion.split("\n") - current_edit = {} - status = "FILEPATH" - for i in range(0, len(lines)): - line = lines[i] - if line == "FILEPATH": - if "FILEPATH" in current_edit: - raw_file_edits.append(current_edit) - current_edit = {} - status = "FILEPATH" - elif line == "REPLACE_ME": - status = "REPLACE_ME" - elif line == "REPLACE_WITH": - status = "REPLACE_WITH" - elif status == "FILEPATH": - current_edit["filepath"] = line - elif status == "REPLACE_ME": - if "replace_me" in current_edit: - current_edit["replace_me"] += "\n" + line - else: - current_edit["replace_me"] = line - elif status == "REPLACE_WITH": - if "replace_with" in current_edit: - current_edit["replace_with"] += "\n" + line - else: - current_edit["replace_with"] = line - if "filepath" in current_edit: - raw_file_edits.append(current_edit) - - file_edits = [] - for edit in raw_file_edits: - filepath = edit["filepath"] - replace_me = edit["replace_me"] - replace_with = edit["replace_with"] - file_edits.append( - FileEdit(filepath=filepath, range=Range.from_lines_snippet_in_file(content=rif_dict[filepath], snippet=replace_me), replacement=replace_with)) - # ------------------------------ - - self._edit_diffs = [] - for file_edit in file_edits: - diff = await sdk.apply_filesystem_edit(file_edit) - self._edit_diffs.append(diff) - - for filepath in set([file_edit.filepath for file_edit in file_edits]): - await sdk.ide.saveFile(filepath) - await sdk.ide.setFileOpen(filepath) - - return None - - -class StarCoderEditHighlightedCodeStep(Step): - user_input: str - hide = False - _prompt: str = "{code}{user_request}" - - _prompt_and_completion: str = "" - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return (await models.gpt35()).complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - range_in_files = await sdk.ide.getHighlightedCode() - if len(range_in_files) == 0: - # Get the full contents of all open files - files = await sdk.ide.getOpenFiles() - contents = {} - for file in files: - contents[file] = await sdk.ide.readFile(file) - - range_in_files = [RangeInFile.from_entire_file( - filepath, content) for filepath, content in contents.items()] - - rif_with_contents = [] - for range_in_file in range_in_files: - file_contents = await sdk.ide.readRangeInFile(range_in_file) - rif_with_contents.append( - RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) - - rif_dict = {} - for rif in rif_with_contents: - rif_dict[rif.filepath] = rif.contents - - for rif in rif_with_contents: - prompt = self._prompt.format( - code=rif.contents, user_request=self.user_input) - completion = str((await sdk.models.starcoder()).complete(prompt)) - eot_token = "<|endoftext|>" - if completion.endswith(eot_token): - completion = completion[:completion.rindex(eot_token)] - - self._prompt_and_completion += prompt + completion - - await sdk.ide.applyFileSystemEdit( - FileEdit(filepath=rif.filepath, range=rif.range, replacement=completion)) - await sdk.ide.saveFile(rif.filepath) - await sdk.ide.setFileOpen(rif.filepath) - - -class EditHighlightedCodeStep(Step): - user_input: str - hide = True - _prompt: str = dedent("""Below is the code before changes: - -{code} - -This is the user request: - -{user_input} - -This is the code after being changed to perfectly satisfy the user request: - """) - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Editing highlighted code" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - range_in_files = await sdk.ide.getHighlightedCode() - if len(range_in_files) == 0: - # Get the full contents of all open files - files = await sdk.ide.getOpenFiles() - contents = {} - for file in files: - contents[file] = await sdk.ide.readFile(file) - - range_in_files = [RangeInFile.from_entire_file( - filepath, content) for filepath, content in contents.items()] - - await sdk.run_step(EditCodeStep( - range_in_files=range_in_files, prompt=self._prompt.format(code="{code}", user_input=self.user_input))) - - -class FindCodeStep(Step): - prompt: str - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "Finding code" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return await sdk.ide.getOpenFiles() - - -class UserInputStep(Step): - user_input: str - - -class SolveTracebackStep(Step): - traceback: Traceback - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return f"```\n{self.traceback.full_traceback}\n```" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - prompt = dedent("""I ran into this problem with my Python code: - - {traceback} - - Below are the files that might need to be fixed: - - {code} - - This is what the code should be in order to avoid the problem: - """).format(traceback=self.traceback.full_traceback, code="{code}") - - range_in_files = [] - for frame in self.traceback.frames: - content = await sdk.ide.readFile(frame.filepath) - range_in_files.append( - RangeInFile.from_entire_file(frame.filepath, content)) - - await sdk.run_step(EditCodeStep( - range_in_files=range_in_files, prompt=prompt)) - return None - - -class MessageStep(Step): - name: str = "Message" - message: str - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return self.message - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return TextObservation(text=self.message) - - -class EmptyStep(Step): - hide: bool = True - - async def describe(self, models: Models) -> Coroutine[str, None, None]: - return "" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - pass diff --git a/continuedev/src/continuedev/libs/steps/migration.py b/continuedev/src/continuedev/libs/steps/migration.py deleted file mode 100644 index 7b70422d..00000000 --- a/continuedev/src/continuedev/libs/steps/migration.py +++ /dev/null @@ -1,26 +0,0 @@ -# When an edit is made to an existing class or a new sqlalchemy class is created, -# this should be kicked off. - -from ...models.filesystem import RangeInFile -from .main import EditCodeStep, RunCommandStep -from ...core.main import Step - - -class MigrationStep(Step): - name: str = "Create and run an alembic migration." - - edited_file: str - - async def run(self, sdk): - recent_edits = await sdk.ide.get_recent_edits(self.edited_file) - recent_edits_string = "\n\n".join( - map(lambda x: x.to_string(), recent_edits)) - description = await (await sdk.models.gpt35()).complete(f"{recent_edits_string}\n\nGenerate a short description of the migration made in the above changes:\n") - await sdk.run_step(RunCommandStep(cmd=f"cd libs && poetry run alembic revision --autogenerate -m {description}")) - migration_file = f"libs/alembic/versions/{?}.py" - contents = await sdk.ide.readFile(migration_file) - await sdk.run_step(EditCodeStep( - range_in_files=[RangeInFile.from_entire_file(migration_file, contents)], - prompt=f"Here are the changes made to the sqlalchemy classes:\n\n{recent_edits_string}\n\nThis is the generated migration file:\n\n{{code}}\n\nReview the migration file to make sure it correctly reflects the changes made to the sqlalchemy classes.", - )) - await sdk.run_step(RunCommandStep(cmd="cd libs && poetry run alembic upgrade head")) diff --git a/continuedev/src/continuedev/libs/steps/nate.py b/continuedev/src/continuedev/libs/steps/nate.py deleted file mode 100644 index 2f84e9d7..00000000 --- a/continuedev/src/continuedev/libs/steps/nate.py +++ /dev/null @@ -1,214 +0,0 @@ -from textwrap import dedent -import time -from typing import Coroutine, Union - -from ...models.filesystem import RangeInFile -from ...models.filesystem_edit import AddDirectory, AddFile -from ...core.observation import Observation, TextObservation -from ...core.main import Step, ContinueSDK -from .main import RunCommandStep -from .core.core import WaitForUserConfirmationStep, EditCodeStep, EditFileStep -import os - - -class WritePytestsStep(Step): - for_filepath: Union[str, None] = None - instructions: str = "Write unit tests for this file." - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - if self.for_filepath is None: - self.for_filepath = (await sdk.ide.getOpenFiles())[0] - - filename = os.path.basename(self.for_filepath) - dirname = os.path.dirname(self.for_filepath) - - path_dir = os.path.join(dirname, "tests") - if not os.path.exists(path_dir): - await sdk.apply_filesystem_edit(AddDirectory(path=path_dir)) - - path = os.path.join(path_dir, f"test_{filename}") - if os.path.exists(path): - return None - - for_file_contents = await sdk.ide.readFile(self.for_filepath) - - prompt = dedent(f"""This is the file you will write unit tests for: - -```python -{for_file_contents} -``` - -Here are additional instructions: - -"{self.instructions}" - -Here is a complete set of pytest unit tests: - - """) - # tests = (await sdk.models.gpt35()).complete(prompt) - tests = ''' -import pytest - -from ..calculator import Calculator - - -@pytest.fixture -def calculator(): - return Calculator() - - -def test_add(calculator): - assert calculator.add(2, 3) == 5 - assert calculator.add(10, -2) == 8 - assert calculator.add(0, 0) == 0 - - -def test_sub(calculator): - assert calculator.sub(2, 3) == -1 - assert calculator.sub(10, -2) == 12 - assert calculator.sub(0, 0) == 0 - - -def test_mul(calculator): - assert calculator.mul(2, 3) == 6 - assert calculator.mul(10, -2) == -20 - assert calculator.mul(0, 0) == 0 - - -def test_div(calculator): - assert calculator.div(2, 3) == 0.6666666666666666 - assert calculator.div(10, -2) == -5 - assert calculator.div(0, 1) == 0 - - -def test_exp(calculator): - assert calculator.exp(2, 3) == 8 - assert calculator.exp(10, -2) == 0.01 - assert calculator.exp(0, 0) == 1 -''' - time.sleep(3.5) - await sdk.apply_filesystem_edit(AddFile(filepath=path, content=tests)) - - return None - - -class CreatePyplot(Step): - # Wish there was a way to add import, specify dependency - name: str = "Create a pyplot" - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - code = dedent("""import matplotlib.pyplot as plt -import numpy as np - -{instructions} - -plt.xlabel("{x_label}") -plt.ylabel("{y_label}") -plt.title("{title}") -plt.show() - """) - - -class ImplementAbstractMethodStep(Step): - name: str = "Implement abstract method for all subclasses" - method_name: str = "def walk(self, path: str) -> List[str]" - class_name: str = "FileSystem" - - async def run(self, sdk: ContinueSDK): - await sdk.run_step(WaitForUserConfirmationStep(prompt="Detected new abstract method. Implement in all subclasses?")) - implementations = [] - for filepath in ["/Users/natesesti/Desktop/continue/extension/examples/python/filesystem/real.py", "/Users/natesesti/Desktop/continue/extension/examples/python/filesystem/virtual.py"]: - contents = await sdk.ide.readFile(filepath) - implementations.append( - RangeInFile.from_entire_file(filepath, contents)) - - for implementation in implementations: - await sdk.run_step(EditCodeStep( - range_in_files=[implementation], - prompt=f"{{code}}\nRewrite the class, implementing the method `{self.method_name}`.\n", - )) - - -class CreateTableStep(Step): - sql_str: str - name: str = "Create a table" - hide = True - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - # Write the TypeORM entity - entity_name = "Order" - orm_entity = '''import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; - -@Entity() -export class Order { - @PrimaryGeneratedColumn() - order_id: number; - - @Column() - customer_id: number; - - @Column() - order_date: Date; - - @Column() - order_total: number; - - @Column() - shipping_address: string; - - @Column() - billing_address: string; - - @Column() - payment_method: string; - - @Column() - order_status: string; - - @Column() - tracking_number: string; -}''' - time.sleep(2) - # orm_entity = (await sdk.models.gpt35()).complete( - # f"{self.sql_str}\n\nWrite a TypeORM entity called {entity_name} for this table, importing as necessary:") - # (await sdk.models.gpt35()).complete("What is the name of the entity?") - await sdk.apply_filesystem_edit(AddFile(filepath=f"/Users/natesesti/Desktop/continue/extension/examples/python/MyProject/src/entity/{entity_name}.ts", content=orm_entity)) - await sdk.ide.setFileOpen(f"/Users/natesesti/Desktop/continue/extension/examples/python/MyProject/src/entity/{entity_name}.ts", True) - - # Add entity to data-source.ts - await sdk.run_step(EditFileStep( - filepath=f"/Users/natesesti/Desktop/continue/extension/examples/python/MyProject/src/data-source.ts", - prompt=f"{{code}}\nAdd the {entity_name} entity:\n", - )) - - # Generate blank migration for the entity - obs: TextObservation = await sdk.run_step(RunCommandStep( - cmd=f"npx typeorm migration:create ./src/migration/Create{entity_name}Table" - )) - migration_filepath = obs.text.split(" ")[1] - - # Wait for user input - await sdk.run_step(WaitForUserConfirmationStep(prompt="Fill in the migration?")) - - # Fill in the migration - await sdk.run_step(EditFileStep( - filepath=migration_filepath, - prompt=f"{{code}}\nThis is the table that was created:\n{self.sql_str}\n\nFill in the migration for the table:\n", - )) - - # Run the migration - command_step = RunCommandStep( - cmd=f"""sqlite3 database.sqlite 'CREATE TABLE orders ( - order_id SERIAL PRIMARY KEY, - customer_id INTEGER, - order_date DATE, - order_total NUMERIC, - shipping_address TEXT, - billing_address TEXT, - payment_method TEXT, - order_status TEXT, - tracking_number TEXT -);'""" - ) - command_step._description = "npx typeorm-ts-node-commonjs migration:run -d ./src/data-source.ts" - await sdk.run_step(command_step) diff --git a/continuedev/src/continuedev/libs/steps/pytest.py b/continuedev/src/continuedev/libs/steps/pytest.py deleted file mode 100644 index 2e83ae2d..00000000 --- a/continuedev/src/continuedev/libs/steps/pytest.py +++ /dev/null @@ -1,37 +0,0 @@ -from textwrap import dedent -from ...models.filesystem_edit import AddDirectory, AddFile -from ...core.main import Step, ContinueSDK -import os - - -class WritePytestsStep(Step): - for_filepath: str - - async def run(self, sdk: ContinueSDK): - filename, dirname = os.path.split(self.for_filepath) - - path_dir = os.path.join(dirname, "tests") - if not os.path.exists(path_dir): - await sdk.apply_filesystem_edit(AddDirectory(path=path_dir)) - - path = os.path.join(path_dir, f"test_{filename}") - if os.path.exists(path): - return - - for_file_contents = await sdk.ide.readFile(self.for_filepath) - - prompt = dedent(f"""\ - This is the file you will write unit tests for: - - ```python - {for_file_contents} - ``` - - Here are additional instructions: - - "{self.instructions}" - - Here is a complete set of pytest unit tests: - """) - tests = (await sdk.models.gpt35()).complete(prompt) - await sdk.apply_filesystem_edit(AddFile(filepath=path, content=tests)) diff --git a/continuedev/src/continuedev/libs/steps/react_posthog.py b/continuedev/src/continuedev/libs/steps/react_posthog.py deleted file mode 100644 index e69de29b..00000000 diff --git a/continuedev/src/continuedev/libs/steps/steps_on_startup.py b/continuedev/src/continuedev/libs/steps/steps_on_startup.py deleted file mode 100644 index fd1eb8f0..00000000 --- a/continuedev/src/continuedev/libs/steps/steps_on_startup.py +++ /dev/null @@ -1,30 +0,0 @@ - - -from ...core.main import ContinueSDK, Models, Step -from .main import UserInputStep -from .draft.dlt import CreatePipelineStep - - -step_name_to_step_class = { - "UserInputStep": UserInputStep, - "CreatePipelineStep": CreatePipelineStep -} - - -class StepsOnStartupStep(Step): - hide: bool = True - - async def describe(self, models: Models): - return "Running steps on startup" - - async def run(self, sdk: ContinueSDK): - steps_descriptions = (await sdk.get_config()).steps_on_startup - - for step_name, step_params in steps_descriptions.items(): - try: - step = step_name_to_step_class[step_name](**step_params) - except: - print( - f"Incorrect parameters for step {step_name}. Parameters provided were: {step_params}") - continue - await sdk.run_step(step) diff --git a/continuedev/src/continuedev/libs/steps/ty.py b/continuedev/src/continuedev/libs/steps/ty.py deleted file mode 100644 index 9dde7c86..00000000 --- a/continuedev/src/continuedev/libs/steps/ty.py +++ /dev/null @@ -1,155 +0,0 @@ -import subprocess -from ...models.main import Position, Range -from ...models.filesystem import RangeInFile -from ...models.filesystem_edit import AddDirectory, AddFile, FileEdit -from ...core.observation import DictObservation -from ...core.main import History, Step, Policy -from ...core.sdk import ContinueSDK -from .main import RunCommandStep -from ..steps.core.core import EditCodeStep, WaitForUserConfirmationStep, WaitForUserInputStep - -source_name = "weather_api" - - -class SetupPipelineStep(Step): - - name = "Setup Pipeline" - - api_description: str # e.g. "I want to load data from the weatherapi.com API" - - async def run(self, sdk: ContinueSDK): - # source_name = (await sdk.models.gpt35()).complete( - # f"Write a snake_case name for the data source described by {self.api_description}: ").strip() - filename = f'/Users/natesesti/Desktop/continue/extension/examples/python/{source_name}.py' - - # running commands to get started when creating a new dlt pipeline - process = subprocess.Popen( - '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE) - out, err = process.communicate(f''' - cd /Users/natesesti/Desktop/continue/extension/examples/python && python3 -m venv env && source env/bin/activate && pip install dlt && dlt init {source_name} duckdb -Y -pip install -r requirements.txt && pip install dlt[duckdb]'''.encode()) - process = subprocess.Popen( - '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE) - out, err = process.communicate( - f'''cd /Users/natesesti/Desktop/continue/extension/examples/python && source env/bin/activate && pip install -r requirements.txt'''.encode()) - # await sdk.run_step( - # RunCommandStep(cmd="cd /Users/natesesti/Desktop/continue/extension/examples/python") >> - # RunCommandStep(cmd=f'python3 -m venv env') >> - # RunCommandStep(cmd=f'source env/bin/activate') >> - # RunCommandStep(cmd=f'pip install dlt') >> - # RunCommandStep(cmd=f'dlt init {source_name} duckdb') >> - # RunCommandStep(cmd=f'pip install -r requirements') - # ) - - # editing the resource function to call the requested API - await sdk.ide.setFileOpen(filename) - contents = await sdk.ide.readFile(filename) - await sdk.run_step(EditCodeStep( - range_in_files=[RangeInFile.from_entire_file(filename, contents)], - prompt=f'{{code}}\n\nRewrite the entire file, editing the resource function to call the API described by this: {self.api_description}' - )) - - # wait for user to put API key in secrets.toml - await sdk.ide.setFileOpen("/Users/natesesti/Desktop/continue/extension/examples/python/.dlt/secrets.toml") - await sdk.run_step(WaitForUserConfirmationStep(prompt=f"Please add the API key to the `secrets.toml` file and then press `Continue`")) - return DictObservation(values={"source_name": source_name}) - - -class ValidatePipelineStep(Step): - - name = "Validate Pipeline" - - async def run(self, sdk: ContinueSDK): - # source_name = sdk.history.last_observation()["source_name"] - filename = f'/Users/natesesti/Desktop/continue/extension/examples/python/{source_name}.py' - - # test that the API call works - await sdk.run_step(RunCommandStep(cmd=f'env/bin/python3 weather_api.py')) - # TODO: validate that the response code is 200 (i.e. successful) else loop - - # remove exit() from the main main function - await sdk.ide.setFileOpen(filename) - contents = await sdk.ide.readFile(filename) - new_contents = contents.replace('exit()', '') - await sdk.apply_filesystem_edit(FileEdit(filepath=filename, range=Range.from_entire_file(contents), replacement=new_contents)) - await sdk.ide.saveFile(filename) - # await sdk.run_step(EditCodeStep( - # range_in_files=[RangeInFile.from_entire_file(filename)], - # prompt=f'Remove exit() from the main function' - # )) - - # test that dlt loads the data into the DuckDB instance - await sdk.run_step(RunCommandStep(cmd=f'env/bin/python3 weather_api.py')) - # TODO: validate that `dlt` outputted success via print(load_info) else loop - - # write Python code in `query.py` that queries the DuckDB instance to validate it worked - query_filename = '/Users/natesesti/Desktop/continue/extension/examples/python/query.py' - - names_query_code = ''' - import duckdb - - # Connect to the DuckDB instance - con = duckdb.connect('weather.duckdb') - - # Query the schema_name.table_name - result = conn.execute("SELECT table_schema || '.' || table_name FROM information_schema.tables WHERE table_schema NOT IN ('information_schema', 'pg_catalog')").fetchall() - - # Print the schema_name.table_name(s) to stdout - for r in result: - print(r[0]) - ''' - # await sdk.apply_filesystem_edit(FileEdit.from_insertion( - # filepath=query_filename, - # position=Position(line=0, character=0), - # content=names_query_code - # )) - # await sdk.run_step(RunCommandStep(cmd=f'env/bin/python3 query.py')) - # TODO: replace with code that grabs all non-dlt `schema_name.table_name`s outputted by previous query - table_name = "weather_api.weather_api_resource" - tables_query_code = f''' -import duckdb - -# connect to DuckDB instance -conn = duckdb.connect(database="weather.duckdb") - -# get table names -rows = conn.execute("SELECT * FROM {table_name};").fetchall() - -# print table names -for row in rows: - print(row) - ''' - await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code)) - await sdk.ide.setFileOpen(query_filename) - # await sdk.apply_filesystem_edit(FileEdit(filepath=query_filename, replacement=tables_query_code, - # range=Range.from_entire_file(content=names_query_code))) - await sdk.run_step(RunCommandStep(cmd=f'env/bin/python3 query.py')) - - -class CreatePipelinePolicy(Policy): - - _current_state: str = "init" - - def next(self, history: History = History.from_empty()) -> "Step": - if self._current_state == "init": - self._current_state = "setup" - return WaitForUserInputStep(prompt="What API do you want to load data from?") - elif self._current_state == "setup": - self._current_state = "validate" - return SetupPipelineStep() - elif self._current_state == "validate": - self._current_state = "done" - return ValidatePipelineStep() - else: - return None - - -class CreatePipelineStep(Step): - - async def run(self, sdk: ContinueSDK): - await sdk.run_step( - WaitForUserInputStep(prompt="What API do you want to load data from?") >> - SetupPipelineStep(api_description="Load data from the WeatherAPI.com API") >> - ValidatePipelineStep() - ) diff --git a/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/README.md b/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/README.md new file mode 100644 index 00000000..df66104f --- /dev/null +++ b/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/README.md @@ -0,0 +1,7 @@ +# ContinueRecipeRecipe + +A recipe for building recipes! + +## How to use this recipe + +This recipe takes a single input, a description of the recipe to be built. diff --git a/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/main.py b/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/main.py new file mode 100644 index 00000000..953fb0c2 --- /dev/null +++ b/continuedev/src/continuedev/recipes/ContinueRecipeRecipe/main.py @@ -0,0 +1,37 @@ +from textwrap import dedent +from ...models.filesystem import RangeInFile +from ...steps.main import EditHighlightedCodeStep +from ...core.main import Step +from ...core.sdk import ContinueSDK + + +class ContinueStepStep(Step): + name: str = "Write your own Continue Step." + prompt: str + + async def run(self, sdk: ContinueSDK): + await sdk.run_step(EditHighlightedCodeStep(user_input=dedent(f"""\ + Here is an example of a Step that runs a command and then edits a file. + + ```python + from ...core.main import Step + from ...core.sdk import ContinueSDK + + class RunCommandAndEditFileStep(Step): + name: str = "Run a command and then edit a file." + command: str + file_path: str + prompt: str + + async def run(self, sdk: ContinueSDK): + await sdk.run([command]) + await sdk.edit_file(filename=self.file_path, prompt=self.prompt) + ``` + + Please edit the code to write your own Step that does the following: + + {self.prommpt} + + It should be a subclass of Step as above, implementing the `run` method, and using pydantic attributes to define the parameters. + + """))) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/README.md b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/README.md new file mode 100644 index 00000000..e69de29b diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py new file mode 100644 index 00000000..c0699cae --- /dev/null +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py @@ -0,0 +1,33 @@ +from textwrap import dedent + +from ...core.main import Step +from ...core.sdk import ContinueSDK +from ...steps.core.core import WaitForUserInputStep +from ...steps.main import MessageStep +from .steps import SetupPipelineStep, ValidatePipelineStep + + +class CreatePipelineRecipe(Step): + hide: bool = True + + async def run(self, sdk: ContinueSDK): + await sdk.run_step( + MessageStep(message=dedent("""\ + This recipe will walk you through the process of creating a dlt pipeline for your chosen data source. With the help of Continue, you will: + - Create a Python virtual environment with dlt installed + - Run `dlt init` to generate a pipeline template + - Write the code to call the API + - Add any required API keys to the `secrets.toml` file + - Test that the API call works + - Load the data into a local DuckDB instance + - Write a query to view the data""")) >> + WaitForUserInputStep(prompt="What API do you want to load data from?") >> + SetupPipelineStep(api_description="WeatherAPI.com API") >> + MessageStep(message=dedent("""\ + This step will validate that your dlt pipeline is working as expected: + - Test that the API call works + - Load the data into a local DuckDB instance + - Write a query to view the data + """)) >> + ValidatePipelineStep() + ) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py new file mode 100644 index 00000000..b480223a --- /dev/null +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -0,0 +1,85 @@ +from textwrap import dedent + +from ...core.sdk import Models +from ...core.observation import DictObservation +from ...models.filesystem_edit import AddFile +from ...core.main import Step +from ...core.sdk import ContinueSDK + + +class SetupPipelineStep(Step): + hide: bool = True + name: str = "Setup dlt Pipeline" + + api_description: str # e.g. "I want to load data from the weatherapi.com API" + + async def describe(self, models: Models): + return dedent(f"""\ + This step will create a new dlt pipeline that loads data from an API, as per your request: + {self.api_description} + """) + + async def run(self, sdk: ContinueSDK): + source_name = (await sdk.models.gpt35()).complete( + f"Write a snake_case name for the data source described by {self.api_description}: ").strip() + filename = f'{source_name}.py' + + # running commands to get started when creating a new dlt pipeline + await sdk.run([ + 'python3 -m venv env', + 'source env/bin/activate', + 'pip install dlt', + f'dlt init {source_name} duckdb', + 'Y', + 'pip install -r requirements.txt' + ]) + + # editing the resource function to call the requested API + await sdk.edit_file( + filename=filename, + prompt=f'Edit the resource function to call the API described by this: {self.api_description}' + ) + + # wait for user to put API key in secrets.toml + await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml") + await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`") + return DictObservation(values={"source_name": source_name}) + + +class ValidatePipelineStep(Step): + hide: bool = True + + async def run(self, sdk: ContinueSDK): + source_name = sdk.history.last_observation().values["source_name"] + filename = f'{source_name}.py' + + # test that the API call works + await sdk.run(f'python3 {filename}') + + # remove exit() from the main main function + await sdk.edit_file( + filename=filename, + prompt='Remove exit() from the main function' + ) + + # load the data into the DuckDB instance + await sdk.run(f'python3 {filename}') + + table_name = f"{source_name}.{source_name}_resource" + tables_query_code = dedent(f'''\ + import duckdb + + # connect to DuckDB instance + conn = duckdb.connect(database="{source_name}.duckdb") + + # get table names + rows = conn.execute("SELECT * FROM {table_name};").fetchall() + + # print table names + for row in rows: + print(row) + ''') + + query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" + await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code)) + await sdk.run('env/bin/python3 query.py') diff --git a/continuedev/src/continuedev/recipes/README.md b/continuedev/src/continuedev/recipes/README.md new file mode 100644 index 00000000..5e01cc97 --- /dev/null +++ b/continuedev/src/continuedev/recipes/README.md @@ -0,0 +1,9 @@ +# This is a collaborative collection of Continue recipes + +Recipes here will automatically be made available in the [Continue VS Code extension](https://marketplace.visualstudio.com/items?itemName=Continue.continue). + +The `recipes` folder contains all recipes, each with the same structure. **If you wish to create your own recipe, please do the following:** + +1. Create a new subfolder in `recipes`, with the name of your recipe (for example `MyNewRecipe`). +2. Make 2 files in this folder: 1) a `README.md` describing your recipe and how to use it and 2) a `main.py` including a single class with the name of your recipe (e.g. `MyNewRecipe`). +3. Write any utility code other than the main recipe class in a separate file, which you can import in `main.py`. Particularly if you decide to break the recipe into multiple sub-steps, try to keep these separate. diff --git a/continuedev/src/continuedev/recipes/TemplateRecipe/README.md b/continuedev/src/continuedev/recipes/TemplateRecipe/README.md new file mode 100644 index 00000000..91d1123b --- /dev/null +++ b/continuedev/src/continuedev/recipes/TemplateRecipe/README.md @@ -0,0 +1,7 @@ +# TemplateRecipe + +This folder is a template that you can copy to create your own recipe. + +## How to use this recipe + +Explain here what users should know when using your recipe. What inputs does it have and what actions will it perform? diff --git a/continuedev/src/continuedev/recipes/TemplateRecipe/main.py b/continuedev/src/continuedev/recipes/TemplateRecipe/main.py new file mode 100644 index 00000000..94675725 --- /dev/null +++ b/continuedev/src/continuedev/recipes/TemplateRecipe/main.py @@ -0,0 +1,27 @@ +from typing import Coroutine +from continuedev.core import Step, ContinueSDK, Observation, Models + + +class TemplateRecipe(Step): + """ + A simple recipe that appends a print statement to the currently open file. + Use this as a template to create your own! + """ + + # Paremeters for the recipe + name: str + + # A title for the recipe, to be displayed in the GUI + title = "Template Recipe" + + # A description of what the recipe accomplished, to be displayed in the GUI + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return f"Appended a statement to print `Hello, {self.name}!` at the end of the file." + + # The code executed when the recipe is run + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + open_files = await sdk.ide.getOpenFiles() + await sdk.edit_file( + filename=open_files[0], + prompt=f"Append a statement to print `Hello, {self.name}!` at the end of the file." + ) diff --git a/continuedev/src/continuedev/recipes/WritePytestsRecipe/README.md b/continuedev/src/continuedev/recipes/WritePytestsRecipe/README.md new file mode 100644 index 00000000..5ce33ecb --- /dev/null +++ b/continuedev/src/continuedev/recipes/WritePytestsRecipe/README.md @@ -0,0 +1,7 @@ +# CreatePytestsRecipe + +A recipe for writing unit tests in Pytest. + +# How to use this recipe + +Call this recipe with a python file open that you would like to test. It will create tests in a `tests/` folder adjacent to the file with the test file given the same name prepended by `test_`. diff --git a/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py new file mode 100644 index 00000000..a35055f3 --- /dev/null +++ b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py @@ -0,0 +1,37 @@ +from textwrap import dedent +from ...models.filesystem_edit import AddDirectory, AddFile +from ...core.main import Step, ContinueSDK +import os + + +class WritePytestsRecipe(Step): + for_filepath: str + + async def run(self, sdk: ContinueSDK): + filename, dirname = os.path.split(self.for_filepath) + + path_dir = os.path.join(dirname, "tests") + if not os.path.exists(path_dir): + await sdk.apply_filesystem_edit(AddDirectory(path=path_dir)) + + path = os.path.join(path_dir, f"test_{filename}") + if os.path.exists(path): + return + + for_file_contents = await sdk.ide.readFile(self.for_filepath) + + prompt = dedent(f"""\ + This is the file you will write unit tests for: + + ```python + {for_file_contents} + ``` + + Here are additional instructions: + + "{self.instructions}" + + Here is a complete set of pytest unit tests: + """) + tests = (await sdk.models.gpt35()).complete(prompt) + await sdk.apply_filesystem_edit(AddFile(filepath=path, content=tests)) diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index 5598e140..0dbfaf38 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -6,7 +6,6 @@ from ..models.filesystem_edit import FileEditWithFullContents from ..core.policy import DemoPolicy from ..core.main import FullState from ..core.autopilot import Autopilot -from ..libs.steps.nate import ImplementAbstractMethodStep from .ide_protocol import AbstractIdeProtocolServer import asyncio import nest_asyncio @@ -34,11 +33,6 @@ class DemoAutopilot(Autopilot): self._manual_edits_buffer.append(edit) # Note that you're storing a lot of unecessary data here. Can compress into EditDiffs on the spot, and merge. # self._manual_edits_buffer = merge_file_edit(self._manual_edits_buffer, edit) - # FOR DEMO PURPOSES - if edit.fileEdit.filepath.endswith("filesystem.py") and "List" in self.cumulative_edit_string and ":" in edit.fileEdit.replacement: - self.cumulative_edit_string = "" - asyncio.create_task(self.run_from_step( - ImplementAbstractMethodStep())) class SessionManager: diff --git a/continuedev/src/continuedev/steps/__init__.py b/continuedev/src/continuedev/steps/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/continuedev/src/continuedev/steps/__init__.py @@ -0,0 +1 @@ + diff --git a/continuedev/src/continuedev/steps/chroma.py b/continuedev/src/continuedev/steps/chroma.py new file mode 100644 index 00000000..59a8b6e0 --- /dev/null +++ b/continuedev/src/continuedev/steps/chroma.py @@ -0,0 +1,61 @@ +from textwrap import dedent +from typing import Coroutine, Union +from ..core.observation import Observation, TextObservation +from ..core.main import Step, ContinueSDK +from .core.core import EditFileStep +from ..libs.chroma.query import query_codebase_index +from .core.core import EditFileStep + + +class AnswerQuestionChroma(Step): + question: str + _answer: Union[str, None] = None + name: str = "Answer Question" + + async def describe(self, llm) -> Coroutine[str, None, None]: + if self._answer is None: + return f"Answering the question: {self.question}" + else: + return self._answer + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + results = query_codebase_index(self.question) + + code_snippets = "" + + files = [] + for node in results.source_nodes: + resource_name = list(node.node.relationships.values())[0] + filepath = resource_name[:resource_name.index("::")] + files.append(filepath) + code_snippets += f"""{filepath}```\n{node.node.text}\n```\n\n""" + + prompt = dedent(f"""Here are a few snippets of code that might be useful in answering the question: + + {code_snippets} + + Here is the question to answer: + + {self.question} + + Here is the answer:""") + + answer = (await sdk.models.gpt35()).complete(prompt) + print(answer) + self._answer = answer + + await sdk.ide.setFileOpen(files[0]) + + +class EditFileChroma(Step): + request: str + hide: bool = True + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + results = query_codebase_index(self.request) + + resource_name = list( + results.source_nodes[0].node.relationships.values())[0] + filepath = resource_name[:resource_name.index("::")] + + await sdk.run_step(EditFileStep(filepath=filepath, prompt=f"Here is the code:\n\n{{code}}\n\nHere is the user request:\n\n{self.request}\n\nHere is the code after making the requested changes:\n")) diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py new file mode 100644 index 00000000..e54a9a21 --- /dev/null +++ b/continuedev/src/continuedev/steps/core/core.py @@ -0,0 +1,208 @@ +# These steps are depended upon by ContinueSDK +import subprocess +from textwrap import dedent +from typing import Coroutine, List, Union +from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder + +from ...models.filesystem_edit import EditDiff, FileEditWithFullContents, FileSystemEdit +from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents +from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation +from ...core.main import Step, SequentialStep + + +class ContinueSDK: + pass + + +class Models: + pass + + +class ReversibleStep(Step): + async def reverse(self, sdk: ContinueSDK): + raise NotImplementedError + + +class FileSystemEditStep(ReversibleStep): + edit: FileSystemEdit + _diff: Union[EditDiff, None] = None + + hide: bool = True + + async def run(self, sdk: "ContinueSDK") -> Coroutine[Observation, None, None]: + self._diff = await sdk.ide.applyFileSystemEdit(self.edit) + return None + + async def reverse(self, sdk: "ContinueSDK"): + await sdk.ide.applyFileSystemEdit(self._diff.backward) + # Where and when should file saves happen? + + +class ShellCommandsStep(Step): + cmds: List[str] + cwd: str | None = None + name: str = "Run Shell Commands" + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + cmds_str = "\n".join(self.cmds) + return (await models.gpt35()).complete(f"{cmds_str}\n\nSummarize what was done in these shell commands, using markdown bullet points:") + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd + + process = subprocess.Popen( + '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) + + stdin_input = "\n".join(self.cmds) + out, err = process.communicate(stdin_input.encode()) + + # If it fails, return the error + if err is not None and err != "": + return TextObservation(text=err) + + return None + + +class EditCodeStep(Step): + # Might make an even more specific atomic step, which is "apply file edit" + range_in_files: List[RangeInFile] + prompt: str # String with {code} somewhere + name: str = "Edit code" + + _edit_diffs: Union[List[EditDiff], None] = None + _prompt: Union[str, None] = None + _completion: Union[str, None] = None + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + if self._edit_diffs is None: + return "Editing files: " + ", ".join(map(lambda rif: rif.filepath, self.range_in_files)) + elif len(self._edit_diffs) == 0: + return "No edits made" + else: + return (await models.gpt35()).complete(dedent(f"""{self._prompt}{self._completion} + + Maximally concise summary of changes in bullet points (can use markdown): + """)) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + rif_with_contents = [] + for range_in_file in self.range_in_files: + file_contents = await sdk.ide.readRangeInFile(range_in_file) + rif_with_contents.append( + RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) + enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) + code_string = enc_dec.encode() + prompt = self.prompt.format(code=code_string) + + completion = (await sdk.models.gpt35()).complete(prompt) + + # Temporarily doing this to generate description. + self._prompt = prompt + self._completion = completion + + file_edits = enc_dec.decode(completion) + + self._edit_diffs = [] + for file_edit in file_edits: + diff = await sdk.apply_filesystem_edit(file_edit) + self._edit_diffs.append(diff) + + for filepath in set([file_edit.filepath for file_edit in file_edits]): + await sdk.ide.saveFile(filepath) + await sdk.ide.setFileOpen(filepath) + + return None + + +class EditFileStep(Step): + filepath: str + prompt: str + hide: bool = True + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "Editing file: " + self.filepath + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + file_contents = await sdk.ide.readFile(self.filepath) + await sdk.run_step(EditCodeStep( + range_in_files=[RangeInFile.from_entire_file( + self.filepath, file_contents)], + prompt=self.prompt + )) + + +class ManualEditStep(ReversibleStep): + edit_diff: EditDiff + hide: bool = True + + hide: bool = True + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "Manual edit step" + # TODO - only handling FileEdit here, but need all other types of FileSystemEdits + # Also requires the merge_file_edit function + # return llm.complete(dedent(f"""This code was replaced: + + # {self.edit_diff.backward.replacement} + + # With this code: + + # {self.edit_diff.forward.replacement} + + # Maximally concise summary of changes in bullet points (can use markdown): + # """)) + + @classmethod + def from_sequence(cls, edits: List[FileEditWithFullContents]) -> "ManualEditStep": + diffs = [] + for edit in edits: + _, diff = FileSystem.apply_edit_to_str( + edit.fileContents, edit.fileEdit) + diffs.append(diff) + return cls(edit_diff=EditDiff.from_sequence(diffs)) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + return None + + async def reverse(self, sdk: ContinueSDK): + await sdk.ide.applyFileSystemEdit(self.edit_diff.backward) + + +class UserInputStep(Step): + user_input: str + name: str = "User Input" + hide: bool = True + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return self.user_input + + async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: + return UserInputObservation(user_input=self.user_input) + + +class WaitForUserInputStep(Step): + prompt: str + name: str = "Waiting for user input" + + _description: Union[str, None] = None + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return self.prompt + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + self._description = self.prompt + resp = await sdk.wait_for_user_input() + return TextObservation(text=resp) + + +class WaitForUserConfirmationStep(Step): + prompt: str + name: str = "Waiting for user confirmation" + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return self.prompt + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + self._description = self.prompt + resp = await sdk.wait_for_user_input() + return TextObservation(text=resp) diff --git a/continuedev/src/continuedev/steps/draft/abstract_method.py b/continuedev/src/continuedev/steps/draft/abstract_method.py new file mode 100644 index 00000000..f3131c4b --- /dev/null +++ b/continuedev/src/continuedev/steps/draft/abstract_method.py @@ -0,0 +1,19 @@ +from ....core.sdk import ContinueSDK +from ....core.main import Step + + +class ImplementAbstractMethodStep(Step): + name: str = "Implement abstract method for all subclasses" + method_name: str + class_name: str + + async def run(self, sdk: ContinueSDK): + + implementations = await sdk.lsp.go_to_implementations(self.class_name) + + for implementation in implementations: + + await sdk.edit_file( + range_in_files=[implementation.range_in_file], + prompt=f"Implement method `{self.method_name}` for this subclass of `{self.class_name}`", + ) diff --git a/continuedev/src/continuedev/steps/draft/migration.py b/continuedev/src/continuedev/steps/draft/migration.py new file mode 100644 index 00000000..ea538bb7 --- /dev/null +++ b/continuedev/src/continuedev/steps/draft/migration.py @@ -0,0 +1,25 @@ +# When an edit is made to an existing class or a new sqlalchemy class is created, +# this should be kicked off. + +from ..main import EditCodeStep, RunCommandStep +from ...core.main import Step + + +class MigrationStep(Step): + name: str = "Create and run an alembic migration." + + edited_file: str + + async def run(self, sdk): + recent_edits = await sdk.ide.get_recent_edits(self.edited_file) + recent_edits_string = "\n\n".join( + map(lambda x: x.to_string(), recent_edits)) + description = await (await sdk.models.gpt35()).complete(f"{recent_edits_string}\n\nGenerate a short description of the migration made in the above changes:\n") + await sdk.run_step(RunCommandStep(cmd=f"cd libs && poetry run alembic revision --autogenerate -m {description}")) + migration_file = f"libs/alembic/versions/{?}.py" + contents = await sdk.ide.readFile(migration_file) + await sdk.run_step(EditCodeStep( + range_in_files=[RangeInFile.from_entire_file(migration_file, contents)], + prompt=f"Here are the changes made to the sqlalchemy classes:\n\n{recent_edits_string}\n\nThis is the generated migration file:\n\n{{code}}\n\nReview the migration file to make sure it correctly reflects the changes made to the sqlalchemy classes.", + )) + await sdk.run_step(RunCommandStep(cmd="cd libs && poetry run alembic upgrade head")) diff --git a/continuedev/src/continuedev/steps/draft/redux.py b/continuedev/src/continuedev/steps/draft/redux.py new file mode 100644 index 00000000..17506316 --- /dev/null +++ b/continuedev/src/continuedev/steps/draft/redux.py @@ -0,0 +1,47 @@ +from ...core.main import Step +from ...core.sdk import ContinueSDK +from ..core.core import EditFileStep + + +class EditReduxStateStep(Step): + + description: str # e.g. "I want to load data from the weatherapi.com API" + + async def run(self, sdk: ContinueSDK): + # Find the right file to edit + + # RootStore + store_filename = "" + sdk.run_step( + EditFileStep( + filename=store_filename, + prompt=f"Edit the root store to add a new slice for {self.description}" + ) + ) + store_file_contents = await sdk.ide.readFile(store_filename) + + # Selector + selector_filename = "" + sdk.run_step(EditFileStep( + filepath=selector_filename, + prompt=f"Edit the selector to add a new property for {self.description}. The store looks like this: {store_file_contents}" + ) + + # Reducer + reducer_filename = "" + sdk.run_step(EditFileStep( + filepath=reducer_filename, + prompt=f"Edit the reducer to add a new property for {self.description}. The store looks like this: {store_file_contents}" + + """ + Starts with implementing selector + 1. RootStore + 2. Selector + 3. Reducer or entire slice + + Need to first determine whether this is an: + 1. edit + 2. add new reducer and property in existing slice + 3. add whole new slice + 4. build redux from scratch + """ diff --git a/continuedev/src/continuedev/steps/draft/typeorm.py b/continuedev/src/continuedev/steps/draft/typeorm.py new file mode 100644 index 00000000..153c855f --- /dev/null +++ b/continuedev/src/continuedev/steps/draft/typeorm.py @@ -0,0 +1,43 @@ +from textwrap import dedent +from ...core.main import Step +from ...core.sdk import ContinueSDK + + +class CreateTableStep(Step): + sql_str: str + name: str = "Create a table in TypeORM" + + async def run(self, sdk: ContinueSDK): + # Write TypeORM entity + entity_name = self.sql_str.split(" ")[2].capitalize() + await sdk.edit_file( + f"src/entity/{entity_name}.ts", + dedent(f"""\ + {self.sql_str} + + Write a TypeORM entity called {entity_name} for this table, importing as necessary:""") + ) + + # Add entity to data-source.ts + await sdk.edit_file(filepath="src/data-source.ts", prompt=f"Add the {entity_name} entity:") + + # Generate blank migration for the entity + out = await sdk.run(f"npx typeorm migration:create ./src/migration/Create{entity_name}Table") + migration_filepath = out.text.split(" ")[1] + + # Wait for user input + await sdk.wait_for_user_confirmation("Fill in the migration?") + + # Fill in the migration + await sdk.edit_file( + migration_filepath, + dedent(f"""\ + This is the table that was created: + + {self.sql_str} + + Fill in the migration for the table:"""), + ) + + # Run the migration + await sdk.run("npx typeorm-ts-node-commonjs migration:run -d ./src/data-source.ts") diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py new file mode 100644 index 00000000..8588ec92 --- /dev/null +++ b/continuedev/src/continuedev/steps/main.py @@ -0,0 +1,358 @@ +from typing import Coroutine, List, Union + +from pydantic import BaseModel + +from ..libs.util.traceback_parsers import parse_python_traceback +from ..libs.llm import LLM +from ..models.main import Traceback, Range +from ..models.filesystem_edit import EditDiff, FileEdit +from ..models.filesystem import RangeInFile, RangeInFileWithContents +from ..core.observation import Observation, TextObservation, TracebackObservation +from ..libs.llm.prompt_utils import MarkdownStyleEncoderDecoder +from textwrap import dedent +from ..core.main import Step +from ..core.sdk import ContinueSDK, Models +from ..core.observation import Observation +import subprocess +from .core.core import EditCodeStep + + +class RunCodeStep(Step): + cmd: str + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return f"Ran command: `{self.cmd}`" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + result = subprocess.run( + self.cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout = result.stdout.decode("utf-8") + stderr = result.stderr.decode("utf-8") + print(stdout, stderr) + + # If it fails, return the error + tb = parse_python_traceback(stdout) or parse_python_traceback(stderr) + if tb: + return TracebackObservation(traceback=tb) + else: + self.hide = True + return None + + +class Policy(BaseModel): + pass + + +class RunPolicyUntilDoneStep(Step): + policy: "Policy" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + next_step = self.policy.next(sdk.history) + while next_step is not None: + observation = await sdk.run_step(next_step) + next_step = self.policy.next(sdk.history) + return observation + + +class RunCommandStep(Step): + cmd: str + name: str = "Run command" + _description: str = None + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + if self._description is not None: + return self._description + return self.cmd + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + cwd = await sdk.ide.getWorkspaceDirectory() + result = subprocess.run( + self.cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) + stdout = result.stdout.decode("utf-8") + stderr = result.stderr.decode("utf-8") + print(stdout, stderr) + + # If it fails, return the error + if result.returncode != 0: + return TextObservation(text=stderr) + else: + return TextObservation(text=stdout) + + +class FasterEditHighlightedCodeStep(Step): + user_input: str + hide = True + _completion: str = "Edit Code" + _edit_diffs: Union[List[EditDiff], None] = None + _prompt: str = dedent("""\ + You will be given code to edit in order to perfectly satisfy the user request. All the changes you make must be described as replacements, which you should format in the following way: + FILEPATH + + REPLACE_ME + + REPLACE_WITH + + + where and can be multiple lines, but should be the mininum needed to make the edit. Be sure to maintain existing whitespace at the start of lines. + + For example, if you want to replace the code `x = 1` with `x = 2` in main.py, you would write: + FILEPATH + main.py + REPLACE_ME + x = 1 + REPLACE_WITH + x = 2 + If you wanted to delete the code + ``` + def sum(a, b): + return a + b + ``` + in main.py, you would write: + FILEPATH + main.py + REPLACE_ME + def sum(a, b): + return a + b + REPLACE_WITH + + You may need to make multiple edits; respond with exactly as many as needed. + + Below is the code before changes: + + {code} + + This is the user request: "{user_input}" + Here is the description of changes to make: +""") + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "Editing highlighted code" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + range_in_files = await sdk.ide.getHighlightedCode() + if len(range_in_files) == 0: + # Get the full contents of all open files + files = await sdk.ide.getOpenFiles() + contents = {} + for file in files: + contents[file] = await sdk.ide.readFile(file) + + range_in_files = [RangeInFile.from_entire_file( + filepath, content) for filepath, content in contents.items()] + + rif_with_contents = [] + for range_in_file in range_in_files: + file_contents = await sdk.ide.readRangeInFile(range_in_file) + rif_with_contents.append( + RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) + enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) + code_string = enc_dec.encode() + prompt = self._prompt.format( + code=code_string, user_input=self.user_input) + + rif_dict = {} + for rif in rif_with_contents: + rif_dict[rif.filepath] = rif.contents + + completion = (await sdk.models.gpt35()).complete(prompt) + + # Temporarily doing this to generate description. + self._prompt = prompt + self._completion = completion + print(completion) + + # ALTERNATIVE DECODING STEP HERE + raw_file_edits = [] + lines = completion.split("\n") + current_edit = {} + status = "FILEPATH" + for i in range(0, len(lines)): + line = lines[i] + if line == "FILEPATH": + if "FILEPATH" in current_edit: + raw_file_edits.append(current_edit) + current_edit = {} + status = "FILEPATH" + elif line == "REPLACE_ME": + status = "REPLACE_ME" + elif line == "REPLACE_WITH": + status = "REPLACE_WITH" + elif status == "FILEPATH": + current_edit["filepath"] = line + elif status == "REPLACE_ME": + if "replace_me" in current_edit: + current_edit["replace_me"] += "\n" + line + else: + current_edit["replace_me"] = line + elif status == "REPLACE_WITH": + if "replace_with" in current_edit: + current_edit["replace_with"] += "\n" + line + else: + current_edit["replace_with"] = line + if "filepath" in current_edit: + raw_file_edits.append(current_edit) + + file_edits = [] + for edit in raw_file_edits: + filepath = edit["filepath"] + replace_me = edit["replace_me"] + replace_with = edit["replace_with"] + file_edits.append( + FileEdit(filepath=filepath, range=Range.from_lines_snippet_in_file(content=rif_dict[filepath], snippet=replace_me), replacement=replace_with)) + # ------------------------------ + + self._edit_diffs = [] + for file_edit in file_edits: + diff = await sdk.apply_filesystem_edit(file_edit) + self._edit_diffs.append(diff) + + for filepath in set([file_edit.filepath for file_edit in file_edits]): + await sdk.ide.saveFile(filepath) + await sdk.ide.setFileOpen(filepath) + + return None + + +class StarCoderEditHighlightedCodeStep(Step): + user_input: str + hide = False + _prompt: str = "{code}{user_request}" + + _prompt_and_completion: str = "" + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return (await models.gpt35()).complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + range_in_files = await sdk.ide.getHighlightedCode() + if len(range_in_files) == 0: + # Get the full contents of all open files + files = await sdk.ide.getOpenFiles() + contents = {} + for file in files: + contents[file] = await sdk.ide.readFile(file) + + range_in_files = [RangeInFile.from_entire_file( + filepath, content) for filepath, content in contents.items()] + + rif_with_contents = [] + for range_in_file in range_in_files: + file_contents = await sdk.ide.readRangeInFile(range_in_file) + rif_with_contents.append( + RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) + + rif_dict = {} + for rif in rif_with_contents: + rif_dict[rif.filepath] = rif.contents + + for rif in rif_with_contents: + prompt = self._prompt.format( + code=rif.contents, user_request=self.user_input) + completion = str((await sdk.models.starcoder()).complete(prompt)) + eot_token = "<|endoftext|>" + if completion.endswith(eot_token): + completion = completion[:completion.rindex(eot_token)] + + self._prompt_and_completion += prompt + completion + + await sdk.ide.applyFileSystemEdit( + FileEdit(filepath=rif.filepath, range=rif.range, replacement=completion)) + await sdk.ide.saveFile(rif.filepath) + await sdk.ide.setFileOpen(rif.filepath) + + +class EditHighlightedCodeStep(Step): + user_input: str + hide = True + _prompt: str = dedent("""Below is the code before changes: + +{code} + +This is the user request: + +{user_input} + +This is the code after being changed to perfectly satisfy the user request: + """) + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "Editing highlighted code" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + range_in_files = await sdk.ide.getHighlightedCode() + if len(range_in_files) == 0: + # Get the full contents of all open files + files = await sdk.ide.getOpenFiles() + contents = {} + for file in files: + contents[file] = await sdk.ide.readFile(file) + + range_in_files = [RangeInFile.from_entire_file( + filepath, content) for filepath, content in contents.items()] + + await sdk.run_step(EditCodeStep( + range_in_files=range_in_files, prompt=self._prompt.format(code="{code}", user_input=self.user_input))) + + +class FindCodeStep(Step): + prompt: str + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "Finding code" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + return await sdk.ide.getOpenFiles() + + +class UserInputStep(Step): + user_input: str + + +class SolveTracebackStep(Step): + traceback: Traceback + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return f"```\n{self.traceback.full_traceback}\n```" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + prompt = dedent("""I ran into this problem with my Python code: + + {traceback} + + Below are the files that might need to be fixed: + + {code} + + This is what the code should be in order to avoid the problem: + """).format(traceback=self.traceback.full_traceback, code="{code}") + + range_in_files = [] + for frame in self.traceback.frames: + content = await sdk.ide.readFile(frame.filepath) + range_in_files.append( + RangeInFile.from_entire_file(frame.filepath, content)) + + await sdk.run_step(EditCodeStep( + range_in_files=range_in_files, prompt=prompt)) + return None + + +class MessageStep(Step): + name: str = "Message" + message: str + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return self.message + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + return TextObservation(text=self.message) + + +class EmptyStep(Step): + hide: bool = True + + async def describe(self, models: Models) -> Coroutine[str, None, None]: + return "" + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + pass diff --git a/continuedev/src/continuedev/steps/steps_on_startup.py b/continuedev/src/continuedev/steps/steps_on_startup.py new file mode 100644 index 00000000..cd40ff56 --- /dev/null +++ b/continuedev/src/continuedev/steps/steps_on_startup.py @@ -0,0 +1,28 @@ +from ..core.main import ContinueSDK, Models, Step +from .main import UserInputStep +from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe + + +step_name_to_step_class = { + "UserInputStep": UserInputStep, + "CreatePipelineRecipe": CreatePipelineRecipe +} + + +class StepsOnStartupStep(Step): + hide: bool = True + + async def describe(self, models: Models): + return "Running steps on startup" + + async def run(self, sdk: ContinueSDK): + steps_descriptions = (await sdk.get_config()).steps_on_startup + + for step_name, step_params in steps_descriptions.items(): + try: + step = step_name_to_step_class[step_name](**step_params) + except: + print( + f"Incorrect parameters for step {step_name}. Parameters provided were: {step_params}") + continue + await sdk.run_step(step) -- cgit v1.2.3-70-g09d2