diff options
-rw-r--r-- | continuedev/src/continuedev/core/autopilot.py | 20 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/main.py | 17 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/policy.py | 8 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/sdk.py | 4 | ||||
-rw-r--r-- | continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py | 64 | ||||
-rw-r--r-- | extension/package-lock.json | 4 | ||||
-rw-r--r-- | extension/package.json | 2 | ||||
-rw-r--r-- | extension/react-app/src/components/StepContainer.tsx | 22 | ||||
-rw-r--r-- | extension/react-app/src/components/index.ts | 8 | ||||
-rw-r--r-- | extension/react-app/src/tabs/gui.tsx | 17 | ||||
-rw-r--r-- | extension/src/continueIdeClient.ts | 1 | ||||
-rw-r--r-- | extension/src/terminal/terminalEmulator.ts | 5 |
12 files changed, 124 insertions, 48 deletions
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index b227570e..5a6bd2e7 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -83,9 +83,9 @@ class Autopilot(ContinueBaseModel): _step_depth: int = 0 async def retry_at_index(self, index: int): - last_step = self.history.pop_last_step() + step = self.history.timeline[index].step.copy() await self.update_subscribers() - await self._run_singular_step(last_step) + await self._run_singular_step(step) async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: capture_event( @@ -119,7 +119,17 @@ class Autopilot(ContinueBaseModel): observation = InternalErrorObservation( error=error_string, title=e.title) + + # Reveal this step, but hide all of the following steps (its substeps) step.hide = False + i = self.history.get_current_index() + while self.history.timeline[i].step.name != step.name: + self.history.timeline[i].step.hide = True + i -= 1 + + if e.with_step is not None: + await self._run_singular_step(e.with_step) + except Exception as e: # Attach an InternalErrorObservation to the step and unhide it. error_string = '\n\n'.join( @@ -129,7 +139,13 @@ class Autopilot(ContinueBaseModel): observation = InternalErrorObservation( error=error_string, title=e.__repr__()) + + # Reveal this step, but hide all of the following steps (its substeps) step.hide = False + i = self.history.get_current_index() + while self.history.timeline[i].step.name != step.name: + self.history.timeline[i].step.hide = True + i -= 1 self._step_depth -= 1 diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 33e25c93..37d80de3 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -67,11 +67,16 @@ class History(ContinueBaseModel): return None return state.observation - def pop_last_step(self) -> Union[HistoryNode, None]: - if self.current_index < 0: + def pop_step(self, index: int = None) -> Union[HistoryNode, None]: + index = index if index is not None else self.current_index + if index < 0 or self.current_index < 0: return None - node = self.timeline.pop(self.current_index) - self.current_index -= 1 + + node = self.timeline.pop(index) + + if index <= self.current_index: + self.current_index -= 1 + return node.step @classmethod @@ -183,10 +188,12 @@ class Context: class ContinueCustomException(Exception): title: str message: str + with_step: Union[Step, None] - def __init__(self, message: str, title: str = "Error while running step:"): + def __init__(self, message: str, title: str = "Error while running step:", with_step: Union[Step, None] = None): self.message = message self.title = title + self.with_step = with_step HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 4934497d..91ae3c83 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -18,10 +18,10 @@ class DemoPolicy(Policy): # At the very start, run initial Steps spcecified in the config if history.get_current() is None: return ( - MessageStep(name="Welcome to Continue!", message="") >> - # SetupContinueWorkspaceStep() >> - # CreateCodebaseIndexChroma() >> - StepsOnStartupStep()) + # MessageStep(name="Welcome to Continue!", message="") >> + # SetupContinueWorkspaceStep() >> + # CreateCodebaseIndexChroma() >> + StepsOnStartupStep()) observation = history.get_current().observation if observation is not None and isinstance(observation, UserInputObservation): diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index f4aa2b35..76caef02 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -131,5 +131,5 @@ class ContinueSDK(AbstractContinueSDK): # self.__autopilot.set_loading_message(message) raise NotImplementedError() - def raise_exception(self, message: str, title: str): - raise ContinueCustomException(message, title) + def raise_exception(self, message: str, title: str, with_step: Union[Step, None] = None): + raise ContinueCustomException(message, title, with_step) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 9bee4c95..c32ae923 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -1,16 +1,19 @@ import os import subprocess from textwrap import dedent +import time from ...models.main import Range from ...models.filesystem import RangeInFile from ...steps.main import MessageStep from ...core.sdk import Models from ...core.observation import DictObservation, InternalErrorObservation -from ...models.filesystem_edit import AddFile +from ...models.filesystem_edit import AddFile, FileEdit from ...core.main import Step from ...core.sdk import ContinueSDK +AI_ASSISTED_STRING = "(✨ AI-Assisted ✨)" + class SetupPipelineStep(Step): hide: bool = True @@ -25,6 +28,8 @@ class SetupPipelineStep(Step): """) async def run(self, sdk: ContinueSDK): + sdk.context.set("api_description", self.api_description) + 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' @@ -45,14 +50,17 @@ class SetupPipelineStep(Step): - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""), name="Setup Python environment") # editing the resource function to call the requested API - await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022") + await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 29, 0)), "#00ff0022") + # sdk.set_loading_message("Writing code to call the API...") await sdk.edit_file( filename=filename, - prompt=f'Edit the resource function to call the API described by this: {self.api_description}', - name="Edit the resource function to call the API" + prompt=f'Edit the resource function to call the API described by this: {self.api_description}. Do not move or remove the exit() call in __main__.', + name=f"Edit the resource function to call the API {AI_ASSISTED_STRING}" ) + time.sleep(1) + # 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`") @@ -76,20 +84,50 @@ class ValidatePipelineStep(Step): # """))) # test that the API call works - output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API") + output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running `python3 {filename}` to test loading data from the API") # If it fails, return the error if "Traceback" in output: + output = "Traceback" + output.split("Traceback")[-1] + file_content = await sdk.ide.readFile(os.path.join(workspace_dir, filename)) + suggestion = (await sdk.models.gpt35()).complete(dedent(f"""\ + ```python + {file_content} + ``` + This above code is a dlt pipeline that loads data from an API. The function with the @resource decorator is responsible for calling the API and returning the data. While attempting to run the pipeline, the following error occurred: + + ```ascii + {output} + ``` + + This is a brief summary of the error followed by a suggestion on how it can be fixed by editing the resource function:""")) + + api_documentation_url = (await sdk.models.gpt35()).complete(dedent(f"""\ + The API I am trying to call is the '{sdk.context.get('api_description')}'. I tried calling it in the @resource function like this: + ```python + {file_content} + ``` + What is the URL for the API documentation that will help me learn how to make this call? Please format in markdown so I can click the link.""")) + sdk.raise_exception( - title="Error while running pipeline.\nFix the resource function in {filename} and rerun this step", description=output) + title=f"Error while running pipeline.\nFix the resource function in {filename} and rerun this step", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=dedent(f"""\ + {suggestion} + + {api_documentation_url} + + After you've fixed the code, click the retry button at the top of the Validate Pipeline step above."""))) # remove exit() from the main main function - await sdk.edit_file( - filename=filename, - prompt='Remove exit() from the main function', - name="Remove early exit() from main function", - description="Remove the `exit()` call from the main function in the pipeline file so that the data is loaded into DuckDB" - ) + await sdk.run_step(MessageStep(name="Remove early exit() from main function", message="Remove the early exit() from the main function now that we are done testing and want the pipeline to load the data into DuckDB.")) + + contents = await sdk.ide.readFile(os.path.join(workspace_dir, filename)) + replacement = "\n".join( + list(filter(lambda line: line.strip() != "exit()", contents.split("\n")))) + await sdk.ide.applyFileSystemEdit(FileEdit( + filepath=os.path.join(workspace_dir, filename), + replacement=replacement, + range=Range.from_entire_file(contents) + )) # load the data into the DuckDB instance await sdk.run(f'python3 {filename}', name="Load data into DuckDB", description=f"Running python3 {filename} to load data into DuckDB") @@ -109,6 +147,6 @@ class ValidatePipelineStep(Step): print(row) ''') - query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py" + query_filename = os.path.join(workspace_dir, "query.py") await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code), name="Add query.py file", description="Adding a file called `query.py` to the workspace that will run a test query on the DuckDB instance") await sdk.run('env/bin/python3 query.py', name="Run test query", description="Running `env/bin/python3 query.py` to test that the data was loaded into DuckDB as expected") diff --git a/extension/package-lock.json b/extension/package-lock.json index a20be756..0b0e063b 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.20", + "version": "0.0.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.20", + "version": "0.0.23", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 598ae778..c979a435 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Refine code 10x faster", - "version": "0.0.20", + "version": "0.0.23", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index a150e370..8ea54325 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -50,8 +50,9 @@ const StepContainerDiv = styled.div<{ open: boolean }>` /* padding: 8px; */ `; -const HeaderDiv = styled.div` - background-color: ${vscBackgroundTransparent}; +const HeaderDiv = styled.div<{ error: boolean }>` + background-color: ${(props) => + props.error ? "#522" : vscBackgroundTransparent}; display: grid; grid-template-columns: 1fr auto; align-items: center; @@ -124,17 +125,23 @@ function StepContainer(props: StepContainerProps) { > <StepContainerDiv open={open}> <GradientBorder + borderColor={ + props.historyNode.observation?.error ? "#f00" : undefined + } className="overflow-hidden cursor-pointer" onClick={() => setOpen((prev) => !prev)} > - <HeaderDiv> + <HeaderDiv + error={props.historyNode.observation?.error ? true : false} + > <h4 className="m-2"> {open ? ( <ChevronDown size="1.4em" /> ) : ( <ChevronRight size="1.4em" /> )} - {props.historyNode.step.name as any} + {props.historyNode.observation?.title || + (props.historyNode.step.name as any)} </h4> {/* <HeaderButton onClick={(e) => { @@ -171,10 +178,9 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( - <ToggleErrorDiv - title={"Error while running step:"} - error={props.historyNode.observation.error as string} - /> + <pre className="overflow-x-scroll"> + {props.historyNode.observation.error as string} + </pre> ) : ( <ReactMarkdown key={1} className="overflow-scroll"> {props.historyNode.step.description as any} diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index ac5faa41..4966f3e8 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -98,17 +98,21 @@ export const Loader = styled.div` export const GradientBorder = styled.div<{ borderWidth?: string; borderRadius?: string; + borderColor?: string; }>` border-radius: ${(props) => props.borderRadius || "0"}; padding-top: ${(props) => props.borderWidth || "1px"}; padding-bottom: ${(props) => props.borderWidth || "1px"}; - background: linear-gradient( + background: ${(props) => + props.borderColor + ? props.borderColor + : `linear-gradient( 101.79deg, #12887a 0%, #87245c 37.64%, #e12637 65.98%, #ffb215 110.45% - ); + )`}; `; export const MainContainerWithBorder = styled.div<{ borderWidth?: string }>` diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 308dfd57..5c75579b 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -44,6 +44,7 @@ function GUI(props: GUIProps) { // "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`", // }, // observation: { + // title: "ERROR FOUND", // error: // "Traceback (most recent call last):\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n print(sum(first, second))\n ^^^^^^^^^^^^^^^^^^\n File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n return a + b\n ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", // }, @@ -228,9 +229,9 @@ function GUI(props: GUIProps) { setUserInputQueue((queue) => { return [...queue, input]; }); - mainTextInputRef.current.value = ""; - mainTextInputRef.current.style.height = ""; } + mainTextInputRef.current.value = ""; + mainTextInputRef.current.style.height = ""; } setWaitingForSteps(true); @@ -307,13 +308,15 @@ function GUI(props: GUIProps) { }} rows={1} onChange={() => { - let textarea = mainTextInputRef.current!; + const textarea = mainTextInputRef.current!; textarea.style.height = ""; /* Reset the height*/ - textarea.style.height = - Math.min(textarea.scrollHeight - 15, 500) + "px"; + textarea.style.height = `${Math.min( + textarea.scrollHeight - 15, + 500 + )}px`; }} - ></MainTextInput> - <ContinueButton onClick={onMainTextInput}></ContinueButton> + /> + <ContinueButton onClick={onMainTextInput} /> </TopGUIDiv> ); } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index e84602f0..bbaf5f08 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -142,6 +142,7 @@ class IdeProtocolClient { editor.setDecorations( vscode.window.createTextEditorDecorationType({ backgroundColor: color, + isWholeLine: true, }), [range] ); diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 8974b7e3..67b47e2f 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -73,8 +73,9 @@ export class CapturedTerminal { const lines = this.dataBuffer.split("\n"); if ( lines.length > 0 && - lines[lines.length - 1].includes("bash-") && - lines[lines.length - 1].trim().endsWith("$") + (lines[lines.length - 1].includes("bash-") || + lines[lines.length - 1].includes("(main)")) && + lines[lines.length - 1].includes("$") ) { resolve(this.dataBuffer); this.dataBuffer = ""; |