diff options
Diffstat (limited to 'continuedev/src')
15 files changed, 116 insertions, 37 deletions
| diff --git a/continuedev/src/continuedev/core/abstract_sdk.py b/continuedev/src/continuedev/core/abstract_sdk.py index 1c800875..417971cd 100644 --- a/continuedev/src/continuedev/core/abstract_sdk.py +++ b/continuedev/src/continuedev/core/abstract_sdk.py @@ -1,10 +1,10 @@ -from abc import ABC, abstractmethod +from abc import ABC, abstractmethod, abstractproperty  from typing import Coroutine, List, Union  from .config import ContinueConfig  from ..models.filesystem_edit import FileSystemEdit  from .observation import Observation -from .main import History, Step +from .main import ChatMessage, History, Step, ChatMessageRole  """ @@ -83,3 +83,11 @@ class AbstractContinueSDK(ABC):      @abstractmethod      def set_loading_message(self, message: str):          pass + +    @abstractmethod +    def add_chat_context(self, content: str, role: ChatMessageRole = "assistent"): +        pass + +    @abstractproperty +    def chat_context(self) -> List[ChatMessage]: +        pass diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index b82e1fef..c979d53a 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -35,6 +35,7 @@ class Autopilot(ContinueBaseModel):      class Config:          arbitrary_types_allowed = True +        keep_untouched = (cached_property,)      def get_full_state(self) -> FullState:          return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 37d80de3..19b36a6a 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -1,10 +1,18 @@ -from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union +from textwrap import dedent +from typing import Callable, Coroutine, Dict, Generator, List, Literal, Tuple, Union  from ..models.main import ContinueBaseModel  from pydantic import validator  from ..libs.llm import LLM  from .observation import Observation +ChatMessageRole = Literal["assistant", "user", "system"] + + +class ChatMessage(ContinueBaseModel): +    role: ChatMessageRole +    content: str +  class HistoryNode(ContinueBaseModel):      """A point in history, a list of which make up History""" @@ -12,12 +20,25 @@ class HistoryNode(ContinueBaseModel):      observation: Union[Observation, None]      depth: int +    def to_chat_messages(self) -> List[ChatMessage]: +        return self.step.chat_context + [ChatMessage(role="assistant", content=self.step.description)] +  class History(ContinueBaseModel):      """A history of steps taken and their results"""      timeline: List[HistoryNode]      current_index: int +    def to_chat_history(self) -> List[ChatMessage]: +        msgs = [] +        for node in self.timeline: +            if not node.step.hide: +                msgs += [ +                    ChatMessage(role="assistant", content=msg) +                    for msg in node.to_chat_messages() +                ] +        return msgs +      def add_node(self, node: HistoryNode):          self.timeline.insert(self.current_index + 1, node)          self.current_index += 1 @@ -113,6 +134,7 @@ class Step(ContinueBaseModel):      description: Union[str, None] = None      system_message: Union[str, None] = None +    chat_context: List[ChatMessage] = []      class Config:          copy_on_model_validation = False diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index c3f1d188..7661f0c4 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -6,10 +6,11 @@ from ..recipes.CreatePipelineRecipe.main import CreatePipelineRecipe  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, MessageStep, EmptyStep, SetupContinueWorkspaceStep +from ..steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep, StarCoderEditHighlightedCodeStep, EmptyStep, SetupContinueWorkspaceStep  from ..recipes.WritePytestsRecipe.main import WritePytestsRecipe  from ..recipes.ContinueRecipeRecipe.main import ContinueStepStep  from ..steps.comment_code import CommentCodeStep +from ..steps.core.core import MessageStep  class DemoPolicy(Policy): diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index ea90a13a..11127361 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -14,7 +14,7 @@ from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI  from ..libs.llm.openai import OpenAI  from .observation import Observation  from ..server.ide_protocol import AbstractIdeProtocolServer -from .main import Context, ContinueCustomException, History, Step +from .main import Context, ContinueCustomException, History, Step, ChatMessage, ChatMessageRole  from ..steps.core.core import * @@ -136,3 +136,11 @@ 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"): +        self.history.timeline[self.history.current_index].step.chat_context.append( +            ChatMessage(content=content, role=role)) + +    @property +    def chat_context(self) -> List[ChatMessage]: +        return self.history.to_chat_history() diff --git a/continuedev/src/continuedev/libs/llm/__init__.py b/continuedev/src/continuedev/libs/llm/__init__.py index 6bae2222..24fd34be 100644 --- a/continuedev/src/continuedev/libs/llm/__init__.py +++ b/continuedev/src/continuedev/libs/llm/__init__.py @@ -1,4 +1,6 @@ -from typing import Union +from typing import List, Union + +from ...core.main import ChatMessage  from ...models.main import AbstractModel  from pydantic import BaseModel @@ -6,7 +8,7 @@ from pydantic import BaseModel  class LLM(BaseModel):      system_message: Union[str, None] = None -    def complete(self, prompt: str, **kwargs): +    def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs):          """Return the completion of the text with the given temperature."""          raise diff --git a/continuedev/src/continuedev/libs/llm/hf_inference_api.py b/continuedev/src/continuedev/libs/llm/hf_inference_api.py index 734da160..1586c620 100644 --- a/continuedev/src/continuedev/libs/llm/hf_inference_api.py +++ b/continuedev/src/continuedev/libs/llm/hf_inference_api.py @@ -1,3 +1,5 @@ +from typing import List +from ...core.main import ChatMessage  from ..llm import LLM  import requests @@ -9,7 +11,7 @@ class HuggingFaceInferenceAPI(LLM):      api_key: str      model: str = "bigcode/starcoder" -    def complete(self, prompt: str, **kwargs): +    def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs):          """Return the completion of the text with the given temperature."""          API_URL = f"https://api-inference.huggingface.co/models/{self.model}"          headers = { diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index 10801465..da8c5caf 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -1,6 +1,7 @@  import asyncio  import time  from typing import Any, Dict, Generator, List, Union +from ...core.main import ChatMessage  import openai  import aiohttp  from ..llm import LLM @@ -62,7 +63,7 @@ class OpenAI(LLM):              for chunk in generator:                  yield chunk.choices[0].text -    def complete(self, prompt: str, **kwargs) -> str: +    def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> str:          t1 = time.time()          self.completion_count += 1 @@ -70,15 +71,17 @@ class OpenAI(LLM):                  "frequency_penalty": 0, "presence_penalty": 0, "stream": False} | kwargs          if args["model"] == "gpt-3.5-turbo": -            messages = [{ -                "role": "user", -                "content": prompt -            }] +            messages = []              if self.system_message: -                messages.insert(0, { +                messages.append({                      "role": "system",                      "content": self.system_message                  }) +            message += [msg.dict() for msg in with_history] +            messages.append({ +                "role": "user", +                "content": prompt +            })              resp = openai.ChatCompletion.create(                  messages=messages,                  **args, diff --git a/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py b/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py index 5e05b587..e9a998e3 100644 --- a/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py +++ b/continuedev/src/continuedev/recipes/AddTransformRecipe/main.py @@ -3,7 +3,7 @@ from textwrap import dedent  from ...core.main import Step  from ...core.sdk import ContinueSDK  from ...steps.core.core import WaitForUserInputStep -from ...steps.main import MessageStep +from ...steps.core.core import MessageStep  from .steps import SetUpChessPipelineStep, AddTransformStep diff --git a/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py b/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py index f7f5a43b..7bb0fc23 100644 --- a/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/AddTransformRecipe/steps.py @@ -1,7 +1,9 @@  import os  from textwrap import dedent -from ...steps.main import MessageStep +from ...models.main import Range +from ...models.filesystem import RangeInFile +from ...steps.core.core import MessageStep  from ...core.sdk import Models  from ...core.observation import DictObservation  from ...models.filesystem_edit import AddFile @@ -26,7 +28,8 @@ class SetUpChessPipelineStep(Step):              'source env/bin/activate',              'pip install dlt',              'dlt --non-interactive init chess duckdb', -            'pip install -r requirements.txt' +            'pip install -r requirements.txt', +            'pip install pandas streamlit'  # Needed for the pipeline show step later          ], name="Set up Python environment", description=dedent(f"""\              Running the following commands:              - `python3 -m venv env`: Create a Python virtual environment @@ -44,7 +47,8 @@ class AddTransformStep(Step):      async def run(self, sdk: ContinueSDK):          source_name = 'chess' -        filename = f'{source_name}.py' +        filename = f'{source_name}_pipeline.py' +        abs_filepath = os.path.join(sdk.ide.workspace_directory, filename)          await sdk.run_step(MessageStep(message=dedent("""\                  This step will customize your resource function with a transform of your choice: @@ -52,6 +56,13 @@ class AddTransformStep(Step):                  - Load the data into a local DuckDB instance                  - Open up a Streamlit app for you to view the data"""), name="Write transformation function")) +        # Open the file and highlight the function to be edited +        await sdk.ide.setFileOpen(abs_filepath) +        await sdk.ide.highlightCode(range_in_file=RangeInFile( +            filepath=abs_filepath, +            range=Range.from_shorthand(47, 0, 51, 0) +        )) +          with open(os.path.join(os.path.dirname(__file__), 'dlt_transform_docs.md')) as f:              dlt_transform_docs = f.read() @@ -75,4 +86,4 @@ class AddTransformStep(Step):          await sdk.run(f'python3 {filename}', name="Run the pipeline", description=f"Running `python3 {filename}` to load the data into a local DuckDB instance")          # run a streamlit app to show the data -        await sdk.run(f'dlt pipeline {source_name} show', name="Show data in a Streamlit app", description=f"Running `dlt pipeline {source_name} show` to show the data in a Streamlit app, where you can view and play with the data.") +        await sdk.run(f'dlt pipeline {source_name}_pipeline show', name="Show data in a Streamlit app", description=f"Running `dlt pipeline {source_name} show` to show the data in a Streamlit app, where you can view and play with the data.") diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py index 39e1ba42..818168ba 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py @@ -3,7 +3,7 @@ from textwrap import dedent  from ...core.main import Step  from ...core.sdk import ContinueSDK  from ...steps.core.core import WaitForUserInputStep -from ...steps.main import MessageStep +from ...steps.core.core import MessageStep  from .steps import SetupPipelineStep, ValidatePipelineStep, RunQueryStep diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py index 3b9a8c85..ea40a058 100644 --- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py +++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py @@ -5,7 +5,7 @@ import time  from ...models.main import Range  from ...models.filesystem import RangeInFile -from ...steps.main import MessageStep +from ...steps.core.core import MessageStep  from ...core.sdk import Models  from ...core.observation import DictObservation, InternalErrorObservation  from ...models.filesystem_edit import AddFile, FileEdit @@ -51,7 +51,7 @@ class SetupPipelineStep(Step):          # editing the resource function to call the requested API          resource_function_range = Range.from_shorthand(15, 0, 29, 0) -        await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=resource_function_range), "#00ff0022") +        await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=resource_function_range))          # sdk.set_loading_message("Writing code to call the API...")          await sdk.edit_file( diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 5826f15f..f4ea1071 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -138,7 +138,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer):              "sessionId": session_id          }) -    async def highlightCode(self, range_in_file: RangeInFile, color: str): +    async def highlightCode(self, range_in_file: RangeInFile, color: str = "#00ff0022"):          await self._send_json("highlightCode", {              "rangeInFile": range_in_file.dict(),              "color": color diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index dfd765eb..5117d479 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -1,4 +1,5 @@  # These steps are depended upon by ContinueSDK +import os  import subprocess  from textwrap import dedent  from typing import Coroutine, List, Union @@ -23,6 +24,17 @@ class ReversibleStep(Step):          raise NotImplementedError +class MessageStep(Step): +    name: str = "Message" +    message: str + +    async def describe(self, models: Models) -> Coroutine[str, None, None]: +        return self.message + +    async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: +        return TextObservation(text=self.message) + +  class FileSystemEditStep(ReversibleStep):      edit: FileSystemEdit      _diff: Union[EditDiff, None] = None @@ -38,6 +50,13 @@ class FileSystemEditStep(ReversibleStep):          # Where and when should file saves happen? +def output_contains_error(output: str) -> bool: +    return "Traceback" in output or "SyntaxError" in output + + +AI_ASSISTED_STRING = "(✨ AI-Assisted ✨)" + +  class ShellCommandsStep(Step):      cmds: List[str]      cwd: Union[str, None] = None @@ -50,13 +69,26 @@ class ShellCommandsStep(Step):              return f"Error when running shell commands:\n```\n{self._err_text}\n```"          cmds_str = "\n".join(self.cmds) -        return (await models.gpt35()).complete(f"{cmds_str}\n\nSummarize what was done in these shell commands, using markdown bullet points:") +        return 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          for cmd in self.cmds:              output = await sdk.ide.runCommand(cmd) +            if output is not None and output_contains_error(output): +                suggestion = sdk.models.gpt35.complete(dedent(f"""\ +                    While running the command `{cmd}`, the following error occurred: + +                    ```ascii +                    {output} +                    ``` + +                    This is a brief summary of the error followed by a suggestion on how it can be fixed:"""), with_context=sdk.chat_context) + +                sdk.raise_exception( +                    title="Error while running query", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=suggestion) +                )          return TextObservation(text=output) @@ -116,7 +148,7 @@ class Gpt35EditCodeStep(Step):      _prompt_and_completion: str = ""      async def describe(self, models: Models) -> Coroutine[str, None, None]: -        return (await models.gpt35()).complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") +        return 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]:          rif_with_contents = [] diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py index 81a1e3a9..24335b4f 100644 --- a/continuedev/src/continuedev/steps/main.py +++ b/continuedev/src/continuedev/steps/main.py @@ -212,7 +212,7 @@ class StarCoderEditHighlightedCodeStep(Step):      _prompt_and_completion: str = ""      async def describe(self, models: Models) -> Coroutine[str, None, None]: -        return (await models.gpt35()).complete(f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points:") +        return 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() @@ -317,17 +317,6 @@ class SolveTracebackStep(Step):          return None -class MessageStep(Step): -    name: str = "Message" -    message: str - -    async def describe(self, models: Models) -> Coroutine[str, None, None]: -        return self.message - -    async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: -        return TextObservation(text=self.message) - -  class EmptyStep(Step):      hide: bool = True | 
