From 4440f8724f24060f2f3a18f1b6e82c16d08d88aa Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 16:11:50 -0700 Subject: first direction --- continuedev/src/continuedev/steps/core/core.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index aee5bc1d..d4f5aa2e 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -334,3 +334,16 @@ class WaitForUserConfirmationStep(Step): self.description = self.prompt resp = await sdk.wait_for_user_input() return TextObservation(text=resp) + + +def get_python_traceback(output: str) -> str: + if "Traceback" in output: + return output[output.index("Traceback"):] + else: + return None + +def get_javascript_traceback(output: str) -> str: + if "at " in output: + return output[output.index("at "):] + else: + return None \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 38cf45ed81d5828835583fd32193e2e442fbf799 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 16:20:59 -0700 Subject: sending entire output for now --- continuedev/src/continuedev/steps/core/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index d4f5aa2e..bcff83f4 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -338,12 +338,12 @@ class WaitForUserConfirmationStep(Step): def get_python_traceback(output: str) -> str: if "Traceback" in output: - return output[output.index("Traceback"):] + return output else: return None def get_javascript_traceback(output: str) -> str: if "at " in output: - return output[output.index("at "):] + return output else: return None \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f1cffbd1284983840d6c2f3ef8bc1fdc36fac89d Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 18:53:57 -0700 Subject: convert all openai calls to async --- continuedev/src/continuedev/libs/llm/__init__.py | 9 ++--- continuedev/src/continuedev/libs/llm/openai.py | 8 ++--- .../src/continuedev/libs/llm/proxy_server.py | 38 ++++++++++++---------- .../recipes/CreatePipelineRecipe/steps.py | 10 +++--- .../src/continuedev/recipes/DDtoBQRecipe/steps.py | 2 +- .../continuedev/recipes/WritePytestsRecipe/main.py | 2 +- continuedev/src/continuedev/steps/chat.py | 6 ++-- continuedev/src/continuedev/steps/chroma.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 4 +-- .../src/continuedev/steps/draft/migration.py | 2 +- .../src/continuedev/steps/input/nl_multiselect.py | 2 +- continuedev/src/continuedev/steps/main.py | 2 +- continuedev/src/continuedev/steps/react.py | 2 +- .../src/continuedev/steps/search_directory.py | 2 +- 14 files changed, 45 insertions(+), 46 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/llm/__init__.py b/continuedev/src/continuedev/libs/llm/__init__.py index 2986b2c4..108eedf1 100644 --- a/continuedev/src/continuedev/libs/llm/__init__.py +++ b/continuedev/src/continuedev/libs/llm/__init__.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Any, Dict, Generator, List, Union +from typing import Any, Coroutine, Dict, Generator, List, Union from ...core.main import ChatMessage from ...models.main import AbstractModel @@ -9,17 +9,14 @@ from pydantic import BaseModel class LLM(ABC): system_message: Union[str, None] = None - def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs): + async def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> Coroutine[Any, Any, str]: """Return the completion of the text with the given temperature.""" - raise + raise NotImplementedError def stream_chat(self, prompt, with_history: List[ChatMessage] = [], **kwargs) -> Generator[Union[Any, List, Dict], None, None]: """Yield a stream of chat messages.""" raise NotImplementedError - def __call__(self, prompt: str, **kwargs): - return self.complete(prompt, **kwargs) - def with_system_message(self, system_message: Union[str, None]): """Return a new model with the given system message.""" raise NotImplementedError diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index 180ea5f0..bc108129 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -1,7 +1,7 @@ import asyncio from functools import cached_property import time -from typing import Any, Dict, Generator, List, Union +from typing import Any, Coroutine, Dict, Generator, List, Union from ...core.main import ChatMessage import openai import aiohttp @@ -107,7 +107,7 @@ class OpenAI(LLM): for chunk in generator: yield chunk.choices[0].text - def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> str: + async def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> Coroutine[Any, Any, str]: t1 = time.time() self.completion_count += 1 @@ -115,12 +115,12 @@ class OpenAI(LLM): "frequency_penalty": 0, "presence_penalty": 0, "stream": False} | kwargs if args["model"] in CHAT_MODELS: - resp = openai.ChatCompletion.create( + resp = await openai.ChatCompletion.acreate( messages=self.compile_chat_messages(with_history, prompt), **args, ).choices[0].message.content else: - resp = openai.Completion.create( + resp = await openai.Completion.acreate( prompt=prompt, **args, ).choices[0].text diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py index 4ff57101..b2948f9a 100644 --- a/continuedev/src/continuedev/libs/llm/proxy_server.py +++ b/continuedev/src/continuedev/libs/llm/proxy_server.py @@ -1,8 +1,9 @@ from functools import cached_property import json -from typing import Any, Dict, Generator, List, Literal, Union +from typing import Any, Coroutine, Dict, Generator, List, Literal, Union import requests import tiktoken +import aiohttp from ...core.main import ChatMessage from ..llm import LLM @@ -39,16 +40,6 @@ class ProxyServer(LLM): def count_tokens(self, text: str): return len(self.__encoding_for_model.encode(text, disallowed_special=())) - def stream_chat(self, prompt, with_history: List[ChatMessage] = [], **kwargs) -> Generator[Union[Any, List, Dict], None, None]: - resp = requests.post(f"{SERVER_URL}/stream_complete", json={ - "chat_history": self.compile_chat_messages(with_history, prompt), - "model": self.default_model, - "unique_id": self.unique_id, - }, stream=True) - for line in resp.iter_lines(): - if line: - yield line.decode("utf-8") - def __prune_chat_history(self, chat_history: List[ChatMessage], max_tokens: int, tokens_for_completion: int): tokens = tokens_for_completion for i in range(len(chat_history) - 1, -1, -1): @@ -75,11 +66,22 @@ class ProxyServer(LLM): return history - def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> str: + async def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> Coroutine[Any, Any, str]: + async with aiohttp.ClientSession() as session: + async with session.post(f"{SERVER_URL}/complete", json={ + "chat_history": self.compile_chat_messages(with_history, prompt), + "model": self.default_model, + "unique_id": self.unique_id, + }) as resp: + return json.loads(await resp.text()) - resp = requests.post(f"{SERVER_URL}/complete", json={ - "chat_history": self.compile_chat_messages(with_history, prompt), - "model": self.default_model, - "unique_id": self.unique_id, - }) - return json.loads(resp.text) + async def stream_chat(self, prompt, with_history: List[ChatMessage] = [], **kwargs) -> Generator[Union[Any, List, Dict], None, None]: + async with aiohttp.ClientSession() as session: + async with session.post(f"{SERVER_URL}/stream_complete", json={ + "chat_history": self.compile_chat_messages(with_history, prompt), + "model": self.default_model, + "unique_id": self.unique_id, + }) as resp: + async for line in resp.content: + if line: + yield line.decode("utf-8") diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 096b41c6..3fba1112 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -29,8 +29,8 @@ class SetupPipelineStep(Step): async def run(self, sdk: ContinueSDK): sdk.context.set("api_description", self.api_description) - source_name = sdk.models.gpt35.complete( - f"Write a snake_case name for the data source described by {self.api_description}: ").strip() + 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 @@ -91,7 +91,7 @@ class ValidatePipelineStep(Step): if "Traceback" in output or "SyntaxError" in output: output = "Traceback" + output.split("Traceback")[-1] file_content = await sdk.ide.readFile(os.path.join(workspace_dir, filename)) - suggestion = sdk.models.gpt35.complete(dedent(f"""\ + suggestion = await sdk.models.gpt35.complete(dedent(f"""\ ```python {file_content} ``` @@ -103,7 +103,7 @@ class ValidatePipelineStep(Step): 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 = sdk.models.gpt35.complete(dedent(f"""\ + 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} @@ -159,7 +159,7 @@ class RunQueryStep(Step): output = 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", handle_error=False) if "Traceback" in output or "SyntaxError" in output: - suggestion = sdk.models.gpt35.complete(dedent(f"""\ + suggestion = await sdk.models.gpt35.complete(dedent(f"""\ ```python {await sdk.ide.readFile(os.path.join(sdk.ide.workspace_directory, "query.py"))} ``` diff --git a/continuedev/src/continuedev/recipes/DDtoBQRecipe/steps.py b/continuedev/src/continuedev/recipes/DDtoBQRecipe/steps.py index 6db9fd4b..df414e2e 100644 --- a/continuedev/src/continuedev/recipes/DDtoBQRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/DDtoBQRecipe/steps.py @@ -82,7 +82,7 @@ class LoadDataStep(Step): docs = f.read() output = "Traceback" + output.split("Traceback")[-1] - suggestion = sdk.models.default.complete(dedent(f"""\ + suggestion = await sdk.models.default.complete(dedent(f"""\ When trying to load data into BigQuery, the following error occurred: ```ascii diff --git a/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py index 688f44c3..6e1244b3 100644 --- a/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py +++ b/continuedev/src/continuedev/recipes/WritePytestsRecipe/main.py @@ -41,7 +41,7 @@ class WritePytestsRecipe(Step): "{self.user_input}" Here is a complete set of pytest unit tests:""") - tests = sdk.models.gpt35.complete(prompt) + tests = await sdk.models.gpt35.complete(prompt) await sdk.apply_filesystem_edit(AddFile(filepath=path, content=tests)) diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 7cfe7e0c..499d127f 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -11,9 +11,9 @@ class SimpleChatStep(Step): async def run(self, sdk: ContinueSDK): self.description = f"## {self.user_input}\n\n" - for chunk in sdk.models.default.stream_chat(self.user_input, with_history=await sdk.get_chat_context()): + async for chunk in sdk.models.default.stream_chat(self.user_input, with_history=await sdk.get_chat_context()): self.description += chunk await sdk.update_ui() - self.name = sdk.models.gpt35.complete( - f"Write a short title for the following chat message: {self.description}").strip() + self.name = (await sdk.models.gpt35.complete( + f"Write a short title for the following chat message: {self.description}")).strip() diff --git a/continuedev/src/continuedev/steps/chroma.py b/continuedev/src/continuedev/steps/chroma.py index 058455b2..9d085981 100644 --- a/continuedev/src/continuedev/steps/chroma.py +++ b/continuedev/src/continuedev/steps/chroma.py @@ -56,7 +56,7 @@ class AnswerQuestionChroma(Step): Here is the answer:""") - answer = sdk.models.gpt35.complete(prompt) + answer = await sdk.models.gpt35.complete(prompt) # Make paths relative to the workspace directory answer = answer.replace(await sdk.ide.getWorkspaceDirectory(), "") diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index aee5bc1d..eb45d1d3 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -80,7 +80,7 @@ class ShellCommandsStep(Step): for cmd in self.cmds: output = await sdk.ide.runCommand(cmd) if self.handle_error and output is not None and output_contains_error(output): - suggestion = sdk.models.gpt35.complete(dedent(f"""\ + suggestion = await sdk.models.gpt35.complete(dedent(f"""\ While running the command `{cmd}`, the following error occurred: ```ascii @@ -183,7 +183,7 @@ class DefaultModelEditCodeStep(Step): prompt = self._prompt.format( code=rif.contents, user_request=self.user_input, file_prefix=segs[0], file_suffix=segs[1]) - completion = str(sdk.models.default.complete(prompt, with_history=await sdk.get_chat_context())) + completion = str(await sdk.models.default.complete(prompt, with_history=await sdk.get_chat_context())) eot_token = "<|endoftext|>" completion = completion.removesuffix(eot_token) diff --git a/continuedev/src/continuedev/steps/draft/migration.py b/continuedev/src/continuedev/steps/draft/migration.py index 7c4b7eb5..f3b36b5e 100644 --- a/continuedev/src/continuedev/steps/draft/migration.py +++ b/continuedev/src/continuedev/steps/draft/migration.py @@ -13,7 +13,7 @@ class MigrationStep(Step): 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 = sdk.models.gpt35.complete(f"{recent_edits_string}\n\nGenerate a short description of the migration made in the above changes:\n") + description = 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([ "cd libs", "poetry run alembic revision --autogenerate -m " + description, diff --git a/continuedev/src/continuedev/steps/input/nl_multiselect.py b/continuedev/src/continuedev/steps/input/nl_multiselect.py index 36c489c7..aee22866 100644 --- a/continuedev/src/continuedev/steps/input/nl_multiselect.py +++ b/continuedev/src/continuedev/steps/input/nl_multiselect.py @@ -23,6 +23,6 @@ class NLMultiselectStep(Step): if first_try is not None: return first_try - gpt_parsed = sdk.models.gpt35.complete( + gpt_parsed = await sdk.models.gpt35.complete( f"These are the available options are: [{', '.join(self.options)}]. The user requested {user_response}. This is the exact string from the options array that they selected:") return extract_option(gpt_parsed) or self.options[0] diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index 0e42d8bf..3968c4a3 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -145,7 +145,7 @@ class FasterEditHighlightedCodeStep(Step): for rif in rif_with_contents: rif_dict[rif.filepath] = rif.contents - completion = sdk.models.gpt35.complete(prompt) + completion = await sdk.models.gpt35.complete(prompt) # Temporarily doing this to generate description. self._prompt = prompt diff --git a/continuedev/src/continuedev/steps/react.py b/continuedev/src/continuedev/steps/react.py index d825d424..4d310fc8 100644 --- a/continuedev/src/continuedev/steps/react.py +++ b/continuedev/src/continuedev/steps/react.py @@ -27,7 +27,7 @@ class NLDecisionStep(Step): Select the step which should be taken next to satisfy the user input. Say only the name of the selected step. You must choose one:""") - resp = sdk.models.gpt35.complete(prompt).lower() + resp = (await sdk.models.gpt35.complete(prompt)).lower() step_to_run = None for step in self.steps: diff --git a/continuedev/src/continuedev/steps/search_directory.py b/continuedev/src/continuedev/steps/search_directory.py index 9f4594b9..d2966f46 100644 --- a/continuedev/src/continuedev/steps/search_directory.py +++ b/continuedev/src/continuedev/steps/search_directory.py @@ -41,7 +41,7 @@ class WriteRegexPatternStep(Step): async def run(self, sdk: ContinueSDK): # Ask the user for a regex pattern - pattern = sdk.models.gpt35.complete(dedent(f"""\ + pattern = await sdk.models.gpt35.complete(dedent(f"""\ This is the user request: {self.user_request} -- cgit v1.2.3-70-g09d2 From f4664228b0d2a612f5c0cfb35062ae51d57e8a08 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 20:46:10 -0700 Subject: cancel steps without running children --- continuedev/src/continuedev/core/autopilot.py | 19 ++++++++++++++++--- continuedev/src/continuedev/core/main.py | 5 ++++- continuedev/src/continuedev/libs/llm/proxy_server.py | 5 ++++- continuedev/src/continuedev/steps/core/core.py | 6 ++---- continuedev/src/continuedev/steps/main.py | 4 ++-- 5 files changed, 28 insertions(+), 11 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 703a73af..b8f2695d 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -99,9 +99,19 @@ class Autopilot(ContinueBaseModel): async def delete_at_index(self, index: int): self.history.timeline[index].step.hide = True + self.history.timeline[index].deleted = True await self.update_subscribers() async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: + # If a parent step is deleted/cancelled, don't run this step + last_depth = self._step_depth + i = self.history.current_index + while i >= 0 and self.history.timeline[i].depth > last_depth: + if self.history.timeline[i].deleted: + return None + last_depth = self.history.timeline[i].depth + i -= 1 + capture_event(self.continue_sdk.ide.unique_id, 'step run', { 'step_name': step.name, 'params': step.dict()}) @@ -114,7 +124,7 @@ class Autopilot(ContinueBaseModel): await self._run_singular_step(manualEditsStep) # Update history - do this first so we get top-first tree ordering - self.history.add_node(HistoryNode( + index_of_history_node = self.history.add_node(HistoryNode( step=step, observation=None, depth=self._step_depth)) # Call all subscribed callbacks @@ -127,6 +137,10 @@ class Autopilot(ContinueBaseModel): try: observation = await step(self.continue_sdk) except Exception as e: + if self.history.timeline[index_of_history_node].deleted: + # If step was deleted/cancelled, don't show error or allow retry + return None + caught_error = True is_continue_custom_exception = issubclass( @@ -176,8 +190,7 @@ class Autopilot(ContinueBaseModel): # Add observation to history, unless already attached error observation if not caught_error: - self.history.get_last_at_depth( - self._step_depth, include_current=True).observation = observation + self.history.timeline[index_of_history_node].observation = observation await self.update_subscribers() # Update its description diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index f6b26d69..97ef9793 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -18,6 +18,7 @@ class HistoryNode(ContinueBaseModel): step: "Step" observation: Union[Observation, None] depth: int + deleted: bool = False def to_chat_messages(self) -> List[ChatMessage]: if self.step.description is None: @@ -37,9 +38,11 @@ class History(ContinueBaseModel): msgs += node.to_chat_messages() return msgs - def add_node(self, node: HistoryNode): + def add_node(self, node: HistoryNode) -> int: + """ Add node and return the index where it was added """ self.timeline.insert(self.current_index + 1, node) self.current_index += 1 + return self.current_index def get_current(self) -> Union[HistoryNode, None]: if self.current_index < 0: diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py index b2948f9a..5f8efac6 100644 --- a/continuedev/src/continuedev/libs/llm/proxy_server.py +++ b/continuedev/src/continuedev/libs/llm/proxy_server.py @@ -73,7 +73,10 @@ class ProxyServer(LLM): "model": self.default_model, "unique_id": self.unique_id, }) as resp: - return json.loads(await resp.text()) + try: + return json.loads(await resp.text()) + except json.JSONDecodeError: + raise Exception(await resp.text()) async def stream_chat(self, prompt, with_history: List[ChatMessage] = [], **kwargs) -> Generator[Union[Any, List, Dict], None, None]: async with aiohttp.ClientSession() as session: diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index eb45d1d3..59af5f38 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -72,7 +72,7 @@ class ShellCommandsStep(Step): return f"Error when running shell commands:\n```\n{self._err_text}\n```" cmds_str = "\n".join(self.cmds) - return models.gpt35.complete(f"{cmds_str}\n\nSummarize what was done in these shell commands, using markdown bullet points:") + 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 @@ -151,10 +151,8 @@ class DefaultModelEditCodeStep(Step): _prompt_and_completion: str = "" async def describe(self, models: Models) -> Coroutine[str, None, None]: - description = models.gpt35.complete( + description = await models.gpt35.complete( f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points. Be concise and only mention changes made to the commit before, not prefix or suffix:") - # self.name = models.gpt35.complete( - # f"Write a short title for this description: {description}") return description async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index 3968c4a3..b61aa3fe 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -213,7 +213,7 @@ class StarCoderEditHighlightedCodeStep(Step): _prompt_and_completion: str = "" async def describe(self, models: Models) -> Coroutine[str, None, None]: - return models.gpt35.complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") + 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() @@ -247,7 +247,7 @@ class StarCoderEditHighlightedCodeStep(Step): segs = full_file_contents.split(rif.contents) prompt = f"{segs[0]}{segs[1]}" + prompt - completion = str((await sdk.models.starcoder()).complete(prompt)) + completion = str(await sdk.models.starcoder.complete(prompt)) eot_token = "<|endoftext|>" completion = completion.removesuffix(eot_token) -- cgit v1.2.3-70-g09d2 From d4f416b794935f4f5c3607c8569a0c77891c1eb6 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 21:26:17 -0700 Subject: better algorithm for pruning chat context --- continuedev/src/continuedev/core/main.py | 4 ++- continuedev/src/continuedev/core/sdk.py | 19 +++++++------ continuedev/src/continuedev/libs/llm/openai.py | 37 +++++++++++++++++++++----- 3 files changed, 45 insertions(+), 15 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 97ef9793..0c7ec67f 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -11,6 +11,8 @@ ChatMessageRole = Literal["assistant", "user", "system"] class ChatMessage(ContinueBaseModel): role: ChatMessageRole content: str + # A summary for pruning chat context to fit context window. Often the Step name. + summary: str class HistoryNode(ContinueBaseModel): @@ -23,7 +25,7 @@ class HistoryNode(ContinueBaseModel): def to_chat_messages(self) -> List[ChatMessage]: if self.step.description is None: return self.step.chat_context - return self.step.chat_context + [ChatMessage(role="assistant", content=self.step.description)] + return self.step.chat_context + [ChatMessage(role="assistant", content=self.step.description, summary=self.step.name)] class History(ContinueBaseModel): diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 76f72d01..8aea6b7f 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -165,26 +165,29 @@ class ContinueSDK(AbstractContinueSDK): def raise_exception(self, message: str, title: str, with_step: Union[Step, None] = None): raise ContinueCustomException(message, title, with_step) - def add_chat_context(self, content: str, role: ChatMessageRole = "assistent"): + def add_chat_context(self, content: str, summary: Union[str, None] = None, role: ChatMessageRole = "assistent"): self.history.timeline[self.history.current_index].step.chat_context.append( - ChatMessage(content=content, role=role)) + ChatMessage(content=content, role=role, summary=summary)) async def get_chat_context(self) -> List[ChatMessage]: history_context = self.history.to_chat_history() highlighted_code = await self.ide.getHighlightedCode() + + preface = "The following code is highlighted" + if len(highlighted_code) == 0: + preface = "The following file is open" # Get the full contents of all open files files = await self.ide.getOpenFiles() - contents = {} - for file in files: - contents[file] = await self.ide.readFile(file) + if len(files) > 0: + content = await self.ide.readFile(files[0]) + highlighted_code = [ + RangeInFile.from_entire_file(files[0], content)] - highlighted_code = [RangeInFile.from_entire_file( - filepath, content) for filepath, content in contents.items()] for rif in highlighted_code: code = await self.ide.readRangeInFile(rif) history_context.append(ChatMessage( - content=f"The following code is highlighted:\n```\n{code}\n```", role="user")) + content=f"{preface} ({rif.filepath}):\n```\n{code}\n```", role="user", summary=f"{preface}: {rif.filepath}")) return history_context async def update_ui(self): diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index bc108129..d457451f 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -42,12 +42,37 @@ class OpenAI(LLM): return len(self.__encoding_for_model.encode(text, disallowed_special=())) def __prune_chat_history(self, chat_history: List[ChatMessage], max_tokens: int, tokens_for_completion: int): - tokens = tokens_for_completion - for i in range(len(chat_history) - 1, -1, -1): - message = chat_history[i] - tokens += self.count_tokens(message.content) - if tokens > max_tokens: - return chat_history[i + 1:] + total_tokens = tokens_for_completion + \ + sum(self.count_tokens(message.content) for message in chat_history) + + # 1. Replace beyond last 5 messages with summary + i = 0 + while total_tokens > max_tokens and i < len(chat_history) - 5: + message = chat_history[0] + total_tokens -= self.count_tokens(message.content) + total_tokens += self.count_tokens(message.summary) + message.content = message.summary + i += 1 + + # 2. Remove entire messages until the last 5 + while len(chat_history) > 5 and total_tokens > max_tokens: + message = chat_history.pop(0) + total_tokens -= self.count_tokens(message.content) + + # 3. Truncate message in the last 5 + i = 0 + while total_tokens > max_tokens: + message = chat_history[0] + total_tokens -= self.count_tokens(message.content) + total_tokens += self.count_tokens(message.summary) + message.content = message.summary + i += 1 + + # 4. Remove entire messages in the last 5 + while total_tokens > max_tokens and len(chat_history) > 0: + message = chat_history.pop(0) + total_tokens -= self.count_tokens(message.content) + return chat_history def with_system_message(self, system_message: Union[str, None]): -- cgit v1.2.3-70-g09d2 From 0469a72530c6c9bb080fd922db1295ff0643306c Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 23:06:56 -0700 Subject: more rigorous js impl --- continuedev/src/continuedev/steps/core/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index bcff83f4..687d4eb0 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -343,7 +343,7 @@ def get_python_traceback(output: str) -> str: return None def get_javascript_traceback(output: str) -> str: - if "at " in output: - return output - else: - return None \ No newline at end of file + lines = output.splitlines("\n") + first_line = lines[0].split(": ") + if len(lines) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): + return output \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 0990619880953a1d3b0e6c6666acd1030bb14353 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 23:07:53 -0700 Subject: Putting back step_name_to_step_class for dlt --- continuedev/src/continuedev/libs/util/step_name_to_steps.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/util/step_name_to_steps.py b/continuedev/src/continuedev/libs/util/step_name_to_steps.py index 4dd9c430..b2bb838a 100644 --- a/continuedev/src/continuedev/libs/util/step_name_to_steps.py +++ b/continuedev/src/continuedev/libs/util/step_name_to_steps.py @@ -6,13 +6,23 @@ from ...steps.main import EditHighlightedCodeStep from ...steps.chat import SimpleChatStep from ...steps.comment_code import CommentCodeStep from ...steps.feedback import FeedbackStep +from ...recipes.AddTransformRecipe.main import AddTransformRecipe +from ...recipes.CreatePipelineRecipe.main import CreatePipelineRecipe +from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe +from ...recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe +# This mapping is used to convert from string in ContinueConfig json to corresponding Step class. +# Used for example in slash_commands and steps_on_startup step_name_to_step_class = { "UserInputStep": UserInputStep, "EditHighlightedCodeStep": EditHighlightedCodeStep, "SimpleChatStep": SimpleChatStep, "CommentCodeStep": CommentCodeStep, "FeedbackStep": FeedbackStep, + "AddTransformRecipe": AddTransformRecipe, + "CreatePipelineRecipe": CreatePipelineRecipe, + "DDtoBQRecipe": DDtoBQRecipe, + "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe, } @@ -22,4 +32,4 @@ def get_step_from_name(step_name: str, params: Dict) -> Step: except: print( f"Incorrect parameters for step {step_name}. Parameters provided were: {params}") - raise \ No newline at end of file + raise -- cgit v1.2.3-70-g09d2 From 3dae06931b13133a23276ce44db9c9f70153dc29 Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 23:09:05 -0700 Subject: protecting against blank outputs --- continuedev/src/continuedev/steps/core/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 687d4eb0..b4456122 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -344,6 +344,7 @@ def get_python_traceback(output: str) -> str: def get_javascript_traceback(output: str) -> str: lines = output.splitlines("\n") - first_line = lines[0].split(": ") - if len(lines) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): + if len(lines) == 0: + first_line = lines[0].split(": ") + if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): return output \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 87477d87a1d41b1fbb043ed1c112c2dc63087bea Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Thu, 15 Jun 2023 23:10:10 -0700 Subject: returning None in js --- continuedev/src/continuedev/steps/core/core.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index b4456122..af3c6cc2 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -344,7 +344,9 @@ def get_python_traceback(output: str) -> str: def get_javascript_traceback(output: str) -> str: lines = output.splitlines("\n") - if len(lines) == 0: + if len(lines) > 0: first_line = lines[0].split(": ") - if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): - return output \ No newline at end of file + if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): + return output + else: + return None \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 971d530f9ddbe8392627409ab63ed64fa1005a5d Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 23:28:55 -0700 Subject: fixing ChatMessage serialization error --- continuedev/src/continuedev/libs/llm/openai.py | 2 +- continuedev/src/continuedev/libs/llm/proxy_server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index d457451f..ec285d55 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -103,7 +103,7 @@ class OpenAI(LLM): "role": "system", "content": self.system_message }) - history += [msg.dict() for msg in msgs] + history += [{"role": msg.role, "content": msg.content} for msg in msgs] history.append({ "role": "user", "content": prompt diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py index 5f8efac6..4227042f 100644 --- a/continuedev/src/continuedev/libs/llm/proxy_server.py +++ b/continuedev/src/continuedev/libs/llm/proxy_server.py @@ -58,7 +58,7 @@ class ProxyServer(LLM): "role": "system", "content": self.system_message }) - history += [msg.dict() for msg in msgs] + history += [{"role": msg.role, "content": msg.content} for msg in msgs] history.append({ "role": "user", "content": prompt -- cgit v1.2.3-70-g09d2 From 98f39f90c030dd107e0bb317fbd396cd432d7f0b Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 23:32:55 -0700 Subject: patch, on_traceback step --- continuedev/src/continuedev/steps/on_traceback.py | 14 ++++++++++++++ extension/package-lock.json | 4 ++-- extension/package.json | 2 +- .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 81620 -> 83961 bytes 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 continuedev/src/continuedev/steps/on_traceback.py (limited to 'continuedev') diff --git a/continuedev/src/continuedev/steps/on_traceback.py b/continuedev/src/continuedev/steps/on_traceback.py new file mode 100644 index 00000000..de668775 --- /dev/null +++ b/continuedev/src/continuedev/steps/on_traceback.py @@ -0,0 +1,14 @@ +from ..core.main import Step +from ..core.sdk import ContinueSDK +from .chat import SimpleChatStep + + +class DefaultOnTracebackStep(Step): + output: str + name: str = "Help With Traceback" + hide: bool = True + + async def run(self, sdk: ContinueSDK): + sdk.run_step(SimpleChatStep( + name="Help With Traceback", + user_input=f"""I got the following error, can you please help explain how to fix it?\n\n{self.output}""")) diff --git a/extension/package-lock.json b/extension/package-lock.json index e41cd2c2..7e8da126 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.40", + "version": "0.0.44", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.40", + "version": "0.0.44", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 4b199420..8cf50d2a 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.40", + "version": "0.0.44", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl index 2f8f1550..614190c7 100644 Binary files a/extension/scripts/continuedev-0.1.1-py3-none-any.whl and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ -- cgit v1.2.3-70-g09d2 From c188ba78c5efb82b8701d285b11e9d198eda8f86 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 15 Jun 2023 23:42:25 -0700 Subject: handle and explain tracebacks --- continuedev/src/continuedev/core/autopilot.py | 13 ++++-- continuedev/src/continuedev/core/config.py | 7 +++ .../continuedev/libs/util/step_name_to_steps.py | 2 + continuedev/src/continuedev/server/ide.py | 20 ++++++--- extension/src/activation/activate.ts | 32 ++++++++++++++ extension/src/continueIdeClient.ts | 10 ++++- extension/src/terminal/terminalEmulator.ts | 50 +++++++++++++++++----- 7 files changed, 112 insertions(+), 22 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index b8f2695d..3ccce89a 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -13,6 +13,7 @@ from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK import asyncio +from ..libs.util.step_name_to_steps import get_step_from_name class Autopilot(ContinueBaseModel): @@ -88,9 +89,15 @@ class Autopilot(ContinueBaseModel): self._manual_edits_buffer.append(edit) # TODO: 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) - - def handle_traceback(self, traceback: str): - raise NotImplementedError + # Note that this is being overriden to do nothing in DemoAgent + + async def handle_command_output(self, output: str): + is_traceback = False + if is_traceback: + for tb_step in self.continue_sdk.config.on_traceback: + step = get_step_from_name(tb_step.step_name)( + output=output, **tb_step.params) + await self._run_singular_step(step) _step_depth: int = 0 diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index 23be8133..d8b29f5b 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -12,6 +12,11 @@ class SlashCommand(BaseModel): params: Optional[Dict] = {} +class OnTracebackSteps(BaseModel): + step_name: str + params: Optional[Dict] = {} + + class ContinueConfig(BaseModel): """ A pydantic class for the continue config file. @@ -48,6 +53,8 @@ class ContinueConfig(BaseModel): step_name="FeedbackStep", ) ] + on_traceback: Optional[List[OnTracebackSteps]] = [ + OnTracebackSteps(step_name="DefaultOnTracebackStep")] def load_config(config_file: str) -> ContinueConfig: diff --git a/continuedev/src/continuedev/libs/util/step_name_to_steps.py b/continuedev/src/continuedev/libs/util/step_name_to_steps.py index b2bb838a..2c4474af 100644 --- a/continuedev/src/continuedev/libs/util/step_name_to_steps.py +++ b/continuedev/src/continuedev/libs/util/step_name_to_steps.py @@ -10,6 +10,7 @@ from ...recipes.AddTransformRecipe.main import AddTransformRecipe from ...recipes.CreatePipelineRecipe.main import CreatePipelineRecipe from ...recipes.DDtoBQRecipe.main import DDtoBQRecipe from ...recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowRecipe +from ...steps.on_traceback import DefaultOnTracebackStep # This mapping is used to convert from string in ContinueConfig json to corresponding Step class. # Used for example in slash_commands and steps_on_startup @@ -23,6 +24,7 @@ step_name_to_step_class = { "CreatePipelineRecipe": CreatePipelineRecipe, "DDtoBQRecipe": DDtoBQRecipe, "DeployPipelineAirflowRecipe": DeployPipelineAirflowRecipe, + "DefaultOnTracebackStep": DefaultOnTracebackStep, } diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index c53149d8..c66cc142 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -1,5 +1,4 @@ # This is a separate server from server/main.py -import asyncio from functools import cached_property import json import os @@ -10,11 +9,13 @@ from uvicorn.main import Server from ..libs.util.queue import AsyncSubscriptionQueue from ..models.filesystem import FileSystem, RangeInFile, EditDiff, RealFileSystem -from ..models.main import Traceback from ..models.filesystem_edit import AddDirectory, AddFile, DeleteDirectory, DeleteFile, FileSystemEdit, FileEdit, FileEditWithFullContents, RenameDirectory, RenameFile, SequentialFileSystemEdit from pydantic import BaseModel from .gui import SessionManager, session_manager from .ide_protocol import AbstractIdeProtocolServer +import asyncio +import nest_asyncio +nest_asyncio.apply() router = APIRouter(prefix="/ide", tags=["ide"]) @@ -135,6 +136,9 @@ class IdeProtocolServer(AbstractIdeProtocolServer): fileEdits = list( map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"])) self.onFileEdits(fileEdits) + elif message_type == "commandOutput": + output = data["output"] + self.onCommandOutput(output) elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand", "uniqueId"]: self.sub_queue.post(message_type, data) else: @@ -189,11 +193,6 @@ class IdeProtocolServer(AbstractIdeProtocolServer): def onAcceptRejectSuggestion(self, suggestionId: str, accepted: bool): pass - def onTraceback(self, traceback: Traceback): - # Same as below, maybe not every autopilot? - for _, session in self.session_manager.sessions.items(): - session.autopilot.handle_traceback(traceback) - def onFileSystemUpdate(self, update: FileSystemEdit): # Access to Autopilot (so SessionManager) pass @@ -211,6 +210,13 @@ class IdeProtocolServer(AbstractIdeProtocolServer): for _, session in self.session_manager.sessions.items(): session.autopilot.handle_manual_edits(edits) + def onCommandOutput(self, output: str): + # Send the output to ALL autopilots. + # Maybe not ideal behavior + for _, session in self.session_manager.sessions.items(): + asyncio.create_task( + session.autopilot.handle_command_output(output)) + # Request information. Session doesn't matter. async def getOpenFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, OpenFilesResponse, "openFiles") diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 135a8ec7..77010241 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -8,6 +8,7 @@ import * as path from "path"; import IdeProtocolClient from "../continueIdeClient"; import { getContinueServerUrl } from "../bridge"; import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; +import { CapturedTerminal } from "../terminal/terminalEmulator"; export let extensionContext: vscode.ExtensionContext | undefined = undefined; @@ -47,5 +48,36 @@ export function activateExtension( ); })(); + // All opened terminals should be replaced by our own terminal + vscode.window.onDidOpenTerminal((terminal) => { + if (terminal.name === "Continue") { + return; + } + const options = terminal.creationOptions; + const capturedTerminal = new CapturedTerminal({ + ...options, + name: "Continue", + }); + terminal.dispose(); + }); + + // If any terminals are open to start, replace them + vscode.window.terminals.forEach((terminal) => { + if (terminal.name === "Continue") { + return; + } + const options = terminal.creationOptions; + const capturedTerminal = new CapturedTerminal( + { + ...options, + name: "Continue", + }, + (commandOutput: string) => { + ideProtocolClient.sendCommandOutput(commandOutput); + } + ); + terminal.dispose(); + }); + extensionContext = context; } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index ef9a91c8..a889d3dc 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -326,13 +326,19 @@ class IdeProtocolClient { private continueTerminal: CapturedTerminal | undefined; async runCommand(command: string) { - if (!this.continueTerminal) { - this.continueTerminal = new CapturedTerminal("Continue"); + if (!this.continueTerminal || this.continueTerminal.isClosed()) { + this.continueTerminal = new CapturedTerminal({ + name: "Continue", + }); } this.continueTerminal.show(); return await this.continueTerminal.runCommand(command); } + + sendCommandOutput(output: string) { + this.messenger?.send("commandOutput", { output }); + } } export default IdeProtocolClient; diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index b3031baf..35f02ac0 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -62,21 +62,29 @@ export class CapturedTerminal { this.terminal.show(); } + isClosed(): boolean { + return this.terminal.exitStatus !== undefined; + } + private commandQueue: [string, (output: string) => void][] = []; private hasRunCommand: boolean = false; + private dataEndsInPrompt(strippedData: string): boolean { + const lines = this.dataBuffer.split("\n"); + return ( + lines.length > 0 && + (lines[lines.length - 1].includes("bash-") || + lines[lines.length - 1].includes(") $ ")) && + lines[lines.length - 1].includes("$") + ); + } + private async waitForCommandToFinish() { return new Promise((resolve, reject) => { this.onDataListeners.push((data: any) => { const strippedData = stripAnsi(data); this.dataBuffer += strippedData; - const lines = this.dataBuffer.split("\n"); - if ( - lines.length > 0 && - (lines[lines.length - 1].includes("bash-") || - lines[lines.length - 1].includes(") $ ")) && - lines[lines.length - 1].includes("$") - ) { + if (this.dataEndsInPrompt(strippedData)) { resolve(this.dataBuffer); this.dataBuffer = ""; this.onDataListeners = []; @@ -112,8 +120,30 @@ export class CapturedTerminal { private readonly writeEmitter: vscode.EventEmitter; - constructor(terminalName: string) { - this.shellCmd = "bash"; // getDefaultShell(); + private splitByCommandsBuffer: string = ""; + private readonly onCommandOutput: ((output: string) => void) | undefined; + + splitByCommandsListener(data: string) { + // Split the output by commands so it can be sent to Continue Server + + const strippedData = stripAnsi(data); + this.splitByCommandsBuffer += strippedData; + if (this.dataEndsInPrompt(strippedData)) { + if (this.onCommandOutput) { + this.onCommandOutput(this.splitByCommandsBuffer); + } + this.dataBuffer = ""; + } + } + + constructor( + options: { name: string } & Partial, + onCommandOutput?: (output: string) => void + ) { + this.onCommandOutput = onCommandOutput; + + // this.shellCmd = "bash"; // getDefaultShell(); + this.shellCmd = getDefaultShell(); const env = { ...(process.env as any) }; if (os.platform() !== "win32") { @@ -154,7 +184,7 @@ export class CapturedTerminal { // Create and clear the terminal this.terminal = vscode.window.createTerminal({ - name: terminalName, + ...options, pty: newPty, }); this.terminal.show(); -- cgit v1.2.3-70-g09d2 From 7f3fe0c6addc1180c6674cb29f1a6b15e944342d Mon Sep 17 00:00:00 2001 From: Ty Dunn Date: Fri, 16 Jun 2023 00:20:24 -0700 Subject: fixing problems with dlt create recipe --- continuedev/src/continuedev/libs/llm/openai.py | 8 ++++---- continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py | 4 ++-- continuedev/src/continuedev/server/ide_protocol.py | 4 ---- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index ec285d55..345af7b4 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -140,15 +140,15 @@ class OpenAI(LLM): "frequency_penalty": 0, "presence_penalty": 0, "stream": False} | kwargs if args["model"] in CHAT_MODELS: - resp = await openai.ChatCompletion.acreate( + resp = (await openai.ChatCompletion.acreate( messages=self.compile_chat_messages(with_history, prompt), **args, - ).choices[0].message.content + )).choices[0].message.content else: - resp = await openai.Completion.acreate( + resp = (await openai.Completion.acreate( prompt=prompt, **args, - ).choices[0].text + )).choices[0].text t2 = time.time() print("Completion time:", t2 - t1) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 3fba1112..1a756a76 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -49,7 +49,7 @@ 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 - resource_function_range = Range.from_shorthand(15, 0, 29, 0) + resource_function_range = Range.from_shorthand(15, 0, 30, 0) await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=resource_function_range), "#ffa50033") # sdk.set_loading_message("Writing code to call the API...") @@ -64,7 +64,7 @@ class SetupPipelineStep(Step): # 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`") + 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`. Otherwise, type '/edit this API does not require an API key'") sdk.context.set("source_name", source_name) diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 1d98f4a1..2dcedc30 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -35,10 +35,6 @@ class AbstractIdeProtocolServer(ABC): def onAcceptRejectSuggestion(self, suggestionId: str, accepted: bool): """Called when the user accepts or rejects a suggestion""" - @abstractmethod - def onTraceback(self, traceback: Traceback): - """Called when a traceback is received""" - @abstractmethod def onFileSystemUpdate(self, update: FileSystemEdit): """Called when a file system update is received""" -- cgit v1.2.3-70-g09d2 From 5162e4e2fde74a05f9b359c90711f221305ba620 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 00:56:28 -0700 Subject: catching and explaining tracebacks --- continuedev/src/continuedev/core/autopilot.py | 15 ++++++---- continuedev/src/continuedev/core/policy.py | 2 +- .../src/continuedev/libs/util/traceback_parsers.py | 33 ++++++++-------------- continuedev/src/continuedev/server/ide_protocol.py | 4 --- continuedev/src/continuedev/steps/chat.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 16 ----------- continuedev/src/continuedev/steps/main.py | 23 --------------- continuedev/src/continuedev/steps/on_traceback.py | 2 +- extension/src/terminal/terminalEmulator.ts | 18 ++++++------ 9 files changed, 34 insertions(+), 81 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 3ccce89a..782d6270 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -14,6 +14,7 @@ from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK import asyncio from ..libs.util.step_name_to_steps import get_step_from_name +from ..libs.util.traceback_parsers import get_python_traceback, get_javascript_traceback class Autopilot(ContinueBaseModel): @@ -92,12 +93,14 @@ class Autopilot(ContinueBaseModel): # Note that this is being overriden to do nothing in DemoAgent async def handle_command_output(self, output: str): - is_traceback = False - if is_traceback: - for tb_step in self.continue_sdk.config.on_traceback: - step = get_step_from_name(tb_step.step_name)( - output=output, **tb_step.params) - await self._run_singular_step(step) + get_traceback_funcs = [get_python_traceback, get_javascript_traceback] + for get_tb_func in get_traceback_funcs: + traceback = get_tb_func(output) + if traceback is not None: + for tb_step in self.continue_sdk.config.on_traceback: + step = get_step_from_name( + tb_step.step_name, {"output": output, **tb_step.params}) + await self._run_singular_step(step) _step_depth: int = 0 diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 255f598d..1b53834b 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -8,7 +8,7 @@ from ..recipes.DeployPipelineAirflowRecipe.main import DeployPipelineAirflowReci from ..recipes.AddTransformRecipe.main import AddTransformRecipe from .main import Step, Validator, History, Policy from .observation import Observation, TracebackObservation, UserInputObservation -from ..steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep, EmptyStep, SetupContinueWorkspaceStep +from ..steps.main import EditHighlightedCodeStep, SolveTracebackStep from ..recipes.WritePytestsRecipe.main import WritePytestsRecipe from ..recipes.ContinueRecipeRecipe.main import ContinueStepStep from ..steps.comment_code import CommentCodeStep diff --git a/continuedev/src/continuedev/libs/util/traceback_parsers.py b/continuedev/src/continuedev/libs/util/traceback_parsers.py index c31929c1..4e7a24e0 100644 --- a/continuedev/src/continuedev/libs/util/traceback_parsers.py +++ b/continuedev/src/continuedev/libs/util/traceback_parsers.py @@ -1,24 +1,15 @@ -from typing import Union -from ...models.main import Traceback -from boltons import tbutils - - -def sort_func(items): - """Sort a list of items.""" - return sorted(items) - - -def parse_python_traceback(stdout: str) -> Union[Traceback, None]: - """Parse a python traceback from stdout.""" - - # Sometimes paths are not quoted, but they need to be - if "File \"" not in stdout: - stdout = stdout.replace("File ", "File \"").replace( - ", line ", "\", line ") +def get_python_traceback(output: str) -> str: + if "Traceback (most recent call last):" in output or "SyntaxError" in output: + return output + else: + return None - try: - tbutil_parsed_exc = tbutils.ParsedException.from_string(stdout) - return Traceback.from_tbutil_parsed_exc(tbutil_parsed_exc) - except Exception: +def get_javascript_traceback(output: str) -> str: + lines = output.splitlines() + if len(lines) > 0: + first_line = lines[0].split(": ") + if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): + return output + else: return None diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 1d98f4a1..2dcedc30 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -35,10 +35,6 @@ class AbstractIdeProtocolServer(ABC): def onAcceptRejectSuggestion(self, suggestionId: str, accepted: bool): """Called when the user accepts or rejects a suggestion""" - @abstractmethod - def onTraceback(self, traceback: Traceback): - """Called when a traceback is received""" - @abstractmethod def onFileSystemUpdate(self, update: FileSystemEdit): """Called when a file system update is received""" diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 499d127f..90514ad6 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -10,7 +10,7 @@ class SimpleChatStep(Step): name: str = "Chat" async def run(self, sdk: ContinueSDK): - self.description = f"## {self.user_input}\n\n" + self.description = f"```{self.user_input}```\n\n" async for chunk in sdk.models.default.stream_chat(self.user_input, with_history=await sdk.get_chat_context()): self.description += chunk await sdk.update_ui() diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 7f3a93ba..59af5f38 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -332,19 +332,3 @@ class WaitForUserConfirmationStep(Step): self.description = self.prompt resp = await sdk.wait_for_user_input() return TextObservation(text=resp) - - -def get_python_traceback(output: str) -> str: - if "Traceback" in output: - return output - else: - return None - -def get_javascript_traceback(output: str) -> str: - lines = output.splitlines("\n") - if len(lines) > 0: - first_line = lines[0].split(": ") - if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): - return output - else: - return None \ No newline at end of file diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index b61aa3fe..5ba86c53 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -3,7 +3,6 @@ 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 @@ -33,28 +32,6 @@ class SetupContinueWorkspaceStep(Step): }""")) -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 diff --git a/continuedev/src/continuedev/steps/on_traceback.py b/continuedev/src/continuedev/steps/on_traceback.py index de668775..a0c4d07b 100644 --- a/continuedev/src/continuedev/steps/on_traceback.py +++ b/continuedev/src/continuedev/steps/on_traceback.py @@ -9,6 +9,6 @@ class DefaultOnTracebackStep(Step): hide: bool = True async def run(self, sdk: ContinueSDK): - sdk.run_step(SimpleChatStep( + await sdk.run_step(SimpleChatStep( name="Help With Traceback", user_input=f"""I got the following error, can you please help explain how to fix it?\n\n{self.output}""")) diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 35f02ac0..9d90b331 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -70,12 +70,13 @@ export class CapturedTerminal { private hasRunCommand: boolean = false; private dataEndsInPrompt(strippedData: string): boolean { - const lines = this.dataBuffer.split("\n"); + const lines = strippedData.split("\n"); + const last_line = lines[lines.length - 1]; return ( - lines.length > 0 && - (lines[lines.length - 1].includes("bash-") || - lines[lines.length - 1].includes(") $ ")) && - lines[lines.length - 1].includes("$") + (lines.length > 0 && + (last_line.includes("bash-") || last_line.includes(") $ ")) && + last_line.includes("$")) || + (last_line.includes("]> ") && last_line.includes(") [")) ); } @@ -127,12 +128,12 @@ export class CapturedTerminal { // Split the output by commands so it can be sent to Continue Server const strippedData = stripAnsi(data); - this.splitByCommandsBuffer += strippedData; + this.splitByCommandsBuffer += data; if (this.dataEndsInPrompt(strippedData)) { if (this.onCommandOutput) { - this.onCommandOutput(this.splitByCommandsBuffer); + this.onCommandOutput(stripAnsi(this.splitByCommandsBuffer)); } - this.dataBuffer = ""; + this.splitByCommandsBuffer = ""; } } @@ -166,6 +167,7 @@ export class CapturedTerminal { // Pass data through to terminal this.writeEmitter.fire(data); + this.splitByCommandsListener(data); for (let listener of this.onDataListeners) { listener(data); } -- cgit v1.2.3-70-g09d2 From c46d16de43b7079a2dd030058682cd1af8844bf6 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 09:22:08 -0700 Subject: improved traceback parser --- continuedev/src/continuedev/libs/util/traceback_parsers.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/util/traceback_parsers.py b/continuedev/src/continuedev/libs/util/traceback_parsers.py index 4e7a24e0..1885cc79 100644 --- a/continuedev/src/continuedev/libs/util/traceback_parsers.py +++ b/continuedev/src/continuedev/libs/util/traceback_parsers.py @@ -1,6 +1,11 @@ +PYTHON_TRACEBACK_PREFIX = "Traceback (most recent call last):" + + def get_python_traceback(output: str) -> str: - if "Traceback (most recent call last):" in output or "SyntaxError" in output: - return output + if PYTHON_TRACEBACK_PREFIX in output: + return PYTHON_TRACEBACK_PREFIX + output.split(PYTHON_TRACEBACK_PREFIX)[-1] + elif "SyntaxError" in output: + return "SyntaxError" + output.split("SyntaxError")[-1] else: return None -- cgit v1.2.3-70-g09d2 From 149db209603c5de16d4135f1fdc4f19104974861 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 11:40:36 -0700 Subject: dlt demo work --- continuedev/src/continuedev/core/autopilot.py | 4 +++ continuedev/src/continuedev/core/config.py | 1 + .../src/continuedev/libs/util/traceback_parsers.py | 13 ++++++--- .../recipes/CreatePipelineRecipe/main.py | 4 ++- .../recipes/CreatePipelineRecipe/steps.py | 4 +-- extension/react-app/src/index.css | 1 + extension/react-app/src/tabs/gui.tsx | 34 ++++++++++++---------- extension/src/activation/activate.ts | 6 ++++ extension/src/continueIdeClient.ts | 2 +- extension/src/terminal/terminalEmulator.ts | 7 +++-- 10 files changed, 49 insertions(+), 27 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 782d6270..73f46a37 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -113,6 +113,10 @@ class Autopilot(ContinueBaseModel): await self.update_subscribers() async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: + # Allow config to set disallowed steps + if step.__class__.__name__ in self.continue_sdk.config.disallowed_steps: + return None + # If a parent step is deleted/cancelled, don't run this step last_depth = self._step_depth i = self.history.current_index diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index d8b29f5b..8f703758 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -22,6 +22,7 @@ class ContinueConfig(BaseModel): A pydantic class for the continue config file. """ steps_on_startup: Optional[Dict[str, Dict]] = {} + disallowed_steps: Optional[List[str]] = [] server_url: Optional[str] = None allow_anonymous_telemetry: Optional[bool] = True default_model: Literal["gpt-3.5-turbo", diff --git a/continuedev/src/continuedev/libs/util/traceback_parsers.py b/continuedev/src/continuedev/libs/util/traceback_parsers.py index 1885cc79..a2e94c26 100644 --- a/continuedev/src/continuedev/libs/util/traceback_parsers.py +++ b/continuedev/src/continuedev/libs/util/traceback_parsers.py @@ -12,9 +12,14 @@ def get_python_traceback(output: str) -> str: def get_javascript_traceback(output: str) -> str: lines = output.splitlines() - if len(lines) > 0: - first_line = lines[0].split(": ") - if len(lines) > 1 and len(first_line) > 0 and len(first_line[0]) > 0 and "at" in lines[1].lstrip(): - return output + first_line = None + for i in range(len(lines) - 1): + segs = lines[i].split(":") + if len(segs) > 1 and segs[0] != "" and segs[1].startswith(" ") and lines[i + 1].strip().startswith("at"): + first_line = lines[i] + break + + if first_line is not None: + return "\n".join(lines[lines.index(first_line):]) else: return None diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py index 92bddc98..55ef107b 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py @@ -27,5 +27,7 @@ class CreatePipelineRecipe(Step): await sdk.run_step( SetupPipelineStep(api_description=text_observation.text) >> ValidatePipelineStep() >> - RunQueryStep() + RunQueryStep() >> + MessageStep( + name="Congrats!", message="You've successfully created your first dlt pipeline! 🎉") ) diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 1a756a76..91515dc2 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -64,7 +64,7 @@ class SetupPipelineStep(Step): # 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`. Otherwise, type '/edit this API does not require an API key'") + 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`.") sdk.context.set("source_name", source_name) @@ -172,5 +172,5 @@ class RunQueryStep(Step): This is a brief summary of the error followed by a suggestion on how it can be fixed:""")) sdk.raise_exception( - title="Error while running query", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=suggestion) + title="Error while running query", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=suggestion + "\n\nIt is also very likely that no duckdb table was created, which can happen if the resource function did not yield any data. Please make sure that it is yielding data and then rerun this step.") ) diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css index 32a92d0e..db8afab9 100644 --- a/extension/react-app/src/index.css +++ b/extension/react-app/src/index.css @@ -22,6 +22,7 @@ html, body, #root { height: 100%; + background-color: var(--vsc-background); } body { diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 279d052b..994cb896 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -194,7 +194,7 @@ function GUI(props: GUIProps) { if (topGuiDivRef.current) { const timeout = setTimeout(() => { window.scrollTo({ - top: window.outerHeight, + top: topGuiDivRef.current!.offsetHeight, behavior: "smooth", }); }, 200); @@ -206,7 +206,9 @@ function GUI(props: GUIProps) { console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate); client?.onStateUpdate((state) => { // Scroll only if user is at very bottom of the window. - const shouldScrollToBottom = window.outerHeight - window.scrollY < 200; + const shouldScrollToBottom = + topGuiDivRef.current && + topGuiDivRef.current?.offsetHeight - window.scrollY < 100; setWaitingForSteps(state.active); setHistory(state.history); setUserInputQueue(state.user_input_queue); @@ -347,12 +349,12 @@ function GUI(props: GUIProps) { { onMainTextInput(); @@ -365,6 +367,14 @@ function GUI(props: GUIProps) { ); diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 77010241..32726c86 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -59,6 +59,9 @@ export function activateExtension( name: "Continue", }); terminal.dispose(); + if (!ideProtocolClient.continueTerminal) { + ideProtocolClient.continueTerminal = capturedTerminal; + } }); // If any terminals are open to start, replace them @@ -77,6 +80,9 @@ export function activateExtension( } ); terminal.dispose(); + if (!ideProtocolClient.continueTerminal) { + ideProtocolClient.continueTerminal = capturedTerminal; + } }); extensionContext = context; diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index a889d3dc..9a93a4ef 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -323,7 +323,7 @@ class IdeProtocolClient { return rangeInFiles; } - private continueTerminal: CapturedTerminal | undefined; + public continueTerminal: CapturedTerminal | undefined; async runCommand(command: string) { if (!this.continueTerminal || this.continueTerminal.isClosed()) { diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts index 9d90b331..ff2060f8 100644 --- a/extension/src/terminal/terminalEmulator.ts +++ b/extension/src/terminal/terminalEmulator.ts @@ -76,7 +76,8 @@ export class CapturedTerminal { (lines.length > 0 && (last_line.includes("bash-") || last_line.includes(") $ ")) && last_line.includes("$")) || - (last_line.includes("]> ") && last_line.includes(") [")) + (last_line.includes("]> ") && last_line.includes(") [")) || + (last_line.includes(" (") && last_line.includes(")>")) ); } @@ -98,7 +99,7 @@ export class CapturedTerminal { if (!this.hasRunCommand) { this.hasRunCommand = true; // Let the first bash- prompt appear and let python env be opened - await this.waitForCommandToFinish(); + // await this.waitForCommandToFinish(); } if (this.commandQueue.length === 0) { @@ -154,7 +155,7 @@ export class CapturedTerminal { // Create the pseudo terminal this.ptyProcess = pty.spawn(this.shellCmd, [], { name: "xterm-256color", - cols: 160, // TODO: Get size of vscode terminal, and change with resize + cols: 250, // No way to get the size of VS Code terminal, or listen to resize, so make it just bigger than most conceivable VS Code widths rows: 26, cwd: getRootDir(), env, -- cgit v1.2.3-70-g09d2 From f6d940dead08bc065de7717d440bd98d8fc60c79 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 11:55:15 -0700 Subject: patch and get file content in DefaultOnTracebackStep --- continuedev/src/continuedev/core/abstract_sdk.py | 2 +- continuedev/src/continuedev/core/sdk.py | 2 +- continuedev/src/continuedev/steps/on_traceback.py | 9 +++++++++ extension/package-lock.json | 4 ++-- extension/package.json | 2 +- .../scripts/continuedev-0.1.1-py3-none-any.whl | Bin 83961 -> 84291 bytes 6 files changed, 14 insertions(+), 5 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/abstract_sdk.py b/continuedev/src/continuedev/core/abstract_sdk.py index 0658f1b8..017e75ef 100644 --- a/continuedev/src/continuedev/core/abstract_sdk.py +++ b/continuedev/src/continuedev/core/abstract_sdk.py @@ -85,7 +85,7 @@ class AbstractContinueSDK(ABC): pass @abstractmethod - def add_chat_context(self, content: str, role: ChatMessageRole = "assistent"): + def add_chat_context(self, content: str, role: ChatMessageRole = "assistant"): pass @abstractmethod diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 8aea6b7f..7639d010 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -165,7 +165,7 @@ class ContinueSDK(AbstractContinueSDK): def raise_exception(self, message: str, title: str, with_step: Union[Step, None] = None): raise ContinueCustomException(message, title, with_step) - def add_chat_context(self, content: str, summary: Union[str, None] = None, role: ChatMessageRole = "assistent"): + def add_chat_context(self, content: str, summary: Union[str, None] = None, role: ChatMessageRole = "assistant"): self.history.timeline[self.history.current_index].step.chat_context.append( ChatMessage(content=content, role=role, summary=summary)) diff --git a/continuedev/src/continuedev/steps/on_traceback.py b/continuedev/src/continuedev/steps/on_traceback.py index a0c4d07b..053b4ef4 100644 --- a/continuedev/src/continuedev/steps/on_traceback.py +++ b/continuedev/src/continuedev/steps/on_traceback.py @@ -1,3 +1,4 @@ +import os from ..core.main import Step from ..core.sdk import ContinueSDK from .chat import SimpleChatStep @@ -9,6 +10,14 @@ class DefaultOnTracebackStep(Step): hide: bool = True async def run(self, sdk: ContinueSDK): + # Add context for any files in the traceback that are in the workspace + for line in self.output.split("\n"): + segs = line.split(" ") + for seg in segs: + if seg.startswith(os.path.sep) and os.path.exists(seg) and os.path.commonprefix([seg, sdk.ide.workspace_directory]) == sdk.ide.workspace_directory: + file_contents = await sdk.ide.readFile(seg) + await sdk.add_chat_context(f"The contents of {seg}:\n```\n{file_contents}\n```", "", "user") + await sdk.run_step(SimpleChatStep( name="Help With Traceback", user_input=f"""I got the following error, can you please help explain how to fix it?\n\n{self.output}""")) diff --git a/extension/package-lock.json b/extension/package-lock.json index 7e8da126..eac8edf2 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.44", + "version": "0.0.45", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.44", + "version": "0.0.45", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 8cf50d2a..8f7da94d 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.44", + "version": "0.0.45", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl index 614190c7..b0b84230 100644 Binary files a/extension/scripts/continuedev-0.1.1-py3-none-any.whl and b/extension/scripts/continuedev-0.1.1-py3-none-any.whl differ -- cgit v1.2.3-70-g09d2 From 472984ab63461b90cf41b645e49bcc95bdf2ef1e Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 16 Jun 2023 15:17:36 -0700 Subject: fixes --- continuedev/src/continuedev/libs/llm/proxy_server.py | 7 +++++-- continuedev/src/continuedev/server/ide.py | 2 +- extension/react-app/src/components/HeaderButtonWithText.tsx | 6 +++++- extension/react-app/src/components/index.ts | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py index 4227042f..93f2d48a 100644 --- a/continuedev/src/continuedev/libs/llm/proxy_server.py +++ b/continuedev/src/continuedev/libs/llm/proxy_server.py @@ -17,7 +17,7 @@ CHAT_MODELS = { "gpt-3.5-turbo", "gpt-4" } -# SERVER_URL = "http://127.0.0.1:8002" +# SERVER_URL = "http://127.0.0.1:8080" SERVER_URL = "https://proxy-server-l6vsfbzhba-uc.a.run.app" @@ -87,4 +87,7 @@ class ProxyServer(LLM): }) as resp: async for line in resp.content: if line: - yield line.decode("utf-8") + try: + yield line.decode("utf-8") + except json.JSONDecodeError: + raise Exception(str(line)) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index c66cc142..c83fbc8a 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -230,7 +230,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): resp = await self._send_and_receive_json({}, UniqueIdResponse, "uniqueId") return resp.uniqueId - @cached_property + @property def workspace_directory(self) -> str: return asyncio.run(self.getWorkspaceDirectory()) diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index d70a3d70..c4f22211 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -14,7 +14,11 @@ const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { setHover(true)} - onMouseLeave={() => setHover(false)} + onMouseLeave={() => { + setTimeout(() => { + setHover(false); + }, 100); + }} onClick={props.onClick} > diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index 525989af..d99b4d96 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -48,7 +48,7 @@ export const Pre = styled.pre` max-height: 150px; overflow-y: scroll; margin: 0; - background-color: ${secondaryDark}; + background-color: ${vscBackground}; border: none; /* text wrapping */ -- cgit v1.2.3-70-g09d2