From 20644034b9d4286509753876bacfe7ca6ed6392d Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 27 May 2023 00:11:28 -0400 Subject: Refactoring to deal with circularity --- continuedev/src/continuedev/libs/agent.py | 180 +++++++++++++ continuedev/src/continuedev/libs/core.py | 289 +-------------------- continuedev/src/continuedev/libs/env.py | 7 + continuedev/src/continuedev/libs/sdk.py | 39 +++ .../src/continuedev/libs/steps/core/core.py | 91 +++++++ continuedev/src/continuedev/libs/steps/main.py | 25 ++ continuedev/src/continuedev/models/main.py | 5 + continuedev/src/continuedev/server/notebook.py | 9 +- 8 files changed, 357 insertions(+), 288 deletions(-) create mode 100644 continuedev/src/continuedev/libs/agent.py create mode 100644 continuedev/src/continuedev/libs/env.py create mode 100644 continuedev/src/continuedev/libs/sdk.py create mode 100644 continuedev/src/continuedev/libs/steps/core/core.py (limited to 'continuedev') diff --git a/continuedev/src/continuedev/libs/agent.py b/continuedev/src/continuedev/libs/agent.py new file mode 100644 index 00000000..466aa234 --- /dev/null +++ b/continuedev/src/continuedev/libs/agent.py @@ -0,0 +1,180 @@ +import traceback +import time +from typing import Callable, Coroutine, List +from ..models.filesystem_edit import FileEditWithFullContents +from .llm import LLM +from .observation import Observation +from ..server.ide_protocol import AbstractIdeProtocolServer +from .util.queue import AsyncSubscriptionQueue +from ..models.main import ContinueBaseModel +from .core import Policy, History, FullState, Step, HistoryNode +from .steps.core.core import ReversibleStep, ManualEditStep, UserInputStep +from .sdk import ContinueSDK + + +class Agent(ContinueBaseModel): + llm: LLM + policy: Policy + ide: AbstractIdeProtocolServer + history: History = History.from_empty() + continue_sdk: "ContinueSDK" + _on_update_callbacks: List[Callable[[FullState], None]] = [] + + _active: bool = False + _should_halt: bool = False + _main_user_input_queue: List[str] = [] + + _user_input_queue = AsyncSubscriptionQueue() + + class Config: + arbitrary_types_allowed = True + + def get_full_state(self) -> FullState: + return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) + + def on_update(self, callback: Callable[["FullState"], None]): + """Subscribe to changes to state""" + self._on_update_callbacks.append(callback) + + def update_subscribers(self): + full_state = self.get_full_state() + for callback in self._on_update_callbacks: + callback(full_state) + + def __get_step_params(self, step: "Step"): + return ContinueSDK(agent=self, llm=self.llm.with_system_message(step.system_message)) + + def give_user_input(self, input: str, index: int): + self._user_input_queue.post(index, input) + + async def wait_for_user_input(self) -> str: + self._active = False + self.update_subscribers() + await self._user_input_queue.get(self.history.current_index) + self._active = True + self.update_subscribers() + + _manual_edits_buffer: List[FileEditWithFullContents] = [] + + async def reverse_to_index(self, index: int): + try: + while self.history.get_current_index() >= index: + current_step = self.history.get_current().step + self.history.step_back() + if issubclass(current_step.__class__, ReversibleStep): + await current_step.reverse(self.__get_step_params(current_step)) + + self.update_subscribers() + except Exception as e: + print(e) + + def handle_manual_edits(self, edits: List[FileEditWithFullContents]): + for edit in edits: + 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 + + _step_depth: int = 0 + + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: + if not is_future_step: + # Check manual edits buffer, clear out if needed by creating a ManualEditStep + if len(self._manual_edits_buffer) > 0: + manualEditsStep = ManualEditStep.from_sequence( + self._manual_edits_buffer) + self._manual_edits_buffer = [] + await self._run_singular_step(manualEditsStep) + + # Update history - do this first so we get top-first tree ordering + self.history.add_node(HistoryNode( + step=step, observation=None, depth=self._step_depth)) + + # Run step + self._step_depth += 1 + observation = await step(self.__get_step_params(step)) + self._step_depth -= 1 + + # Add observation to history + self.history.get_current().observation = observation + + # Update its description + step._set_description(await step.describe(self.llm)) + + # Call all subscribed callbacks + self.update_subscribers() + + return observation + + async def run_from_step(self, step: "Step"): + # if self._active: + # raise RuntimeError("Agent is already running") + self._active = True + + next_step = step + is_future_step = False + while not (next_step is None or self._should_halt): + try: + if is_future_step: + # If future step, then we are replaying and need to delete the step from history so it can be replaced + self.history.remove_current_and_substeps() + + observation = await self._run_singular_step(next_step, is_future_step) + if next_step := self.policy.next(self.history): + is_future_step = False + elif next_step := self.history.take_next_step(): + is_future_step = True + else: + next_step = None + + except Exception as e: + print( + f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") + next_step = None + + self._active = False + + # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools + for callback in self._on_update_callbacks: + callback(None) + + async def run_from_observation(self, observation: Observation): + next_step = self.policy.next(self.history) + await self.run_from_step(next_step) + + async def run_policy(self): + first_step = self.policy.next(self.history) + await self.run_from_step(first_step) + + async def _request_halt(self): + if self._active: + self._should_halt = True + while self._active: + time.sleep(0.1) + self._should_halt = False + return None + + async def accept_user_input(self, user_input: str): + self._main_user_input_queue.append(user_input) + self.update_subscribers() + + if len(self._main_user_input_queue) > 1: + return + + # await self._request_halt() + # Just run the step that takes user input, and + # then up to the policy to decide how to deal with it. + self._main_user_input_queue.pop(0) + self.update_subscribers() + await self.run_from_step(UserInputStep(user_input=user_input)) + + while len(self._main_user_input_queue) > 0: + await self.run_from_step(UserInputStep( + user_input=self._main_user_input_queue.pop(0))) + + async def accept_refinement_input(self, user_input: str, index: int): + await self._request_halt() + await self.reverse_to_index(index) + await self.run_from_step(UserInputStep(user_input=user_input)) diff --git a/continuedev/src/continuedev/libs/core.py b/continuedev/src/continuedev/libs/core.py index 6a8a83ba..9d432c4b 100644 --- a/continuedev/src/continuedev/libs/core.py +++ b/continuedev/src/continuedev/libs/core.py @@ -1,18 +1,9 @@ -import traceback -import time from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union -from ..models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit -from ..models.filesystem import FileSystem -from pydantic import BaseModel, parse_file_as, validator -from .llm import LLM -from .observation import Observation, UserInputObservation -from ..server.ide_protocol import AbstractIdeProtocolServer -from .util.queue import AsyncSubscriptionQueue - -class ContinueBaseModel(BaseModel): - class Config: - underscore_attrs_are_private = True +from ..models.main import ContinueBaseModel +from pydantic import validator +from .llm import LLM +from .observation import Observation class HistoryNode(ContinueBaseModel): @@ -86,198 +77,11 @@ class Policy(ContinueBaseModel): class ContinueSDK: - """The SDK provided as parameters to a step""" - llm: LLM - ide: AbstractIdeProtocolServer - __agent: "Agent" - - def __init__(self, agent: "Agent", llm: Union[LLM, None] = None): - if llm is None: - self.llm = agent.llm - else: - self.llm = llm - self.ide = agent.ide - self.__agent = agent - - @property - def history(self) -> History: - return self.__agent.history - - async def run_step(self, step: "Step") -> Coroutine[Observation, None, None]: - return await self.__agent._run_singular_step(step) + pass - async def apply_filesystem_edit(self, edit: FileSystemEdit): - await self.run_step(FileSystemEditStep(edit=edit)) - async def wait_for_user_input(self) -> str: - return await self.__agent.wait_for_user_input() - - -class Agent(ContinueBaseModel): - llm: LLM - policy: Policy - ide: AbstractIdeProtocolServer - history: History = History.from_empty() - _on_update_callbacks: List[Callable[["FullState"], None]] = [] - - _active: bool = False - _should_halt: bool = False - _main_user_input_queue: List[str] = [] - - _user_input_queue = AsyncSubscriptionQueue() - - class Config: - arbitrary_types_allowed = True - - def get_full_state(self) -> FullState: - return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) - - def on_update(self, callback: Callable[["FullState"], None]): - """Subscribe to changes to state""" - self._on_update_callbacks.append(callback) - - def update_subscribers(self): - full_state = self.get_full_state() - for callback in self._on_update_callbacks: - callback(full_state) - - def __get_step_params(self, step: "Step"): - return ContinueSDK(agent=self, llm=self.llm.with_system_message(step.system_message)) - - def give_user_input(self, input: str, index: int): - self._user_input_queue.post(index, input) - - async def wait_for_user_input(self) -> str: - self._active = False - self.update_subscribers() - await self._user_input_queue.get(self.history.current_index) - self._active = True - self.update_subscribers() - - _manual_edits_buffer: List[FileEditWithFullContents] = [] - - async def reverse_to_index(self, index: int): - try: - while self.history.get_current_index() >= index: - current_step = self.history.get_current().step - self.history.step_back() - if issubclass(current_step.__class__, ReversibleStep): - await current_step.reverse(self.__get_step_params(current_step)) - - self.update_subscribers() - except Exception as e: - print(e) - - def handle_manual_edits(self, edits: List[FileEditWithFullContents]): - for edit in edits: - 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 - - _step_depth: int = 0 - - async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: - if not is_future_step: - # Check manual edits buffer, clear out if needed by creating a ManualEditStep - if len(self._manual_edits_buffer) > 0: - manualEditsStep = ManualEditStep.from_sequence( - self._manual_edits_buffer) - self._manual_edits_buffer = [] - await self._run_singular_step(manualEditsStep) - - # Update history - do this first so we get top-first tree ordering - self.history.add_node(HistoryNode( - step=step, observation=None, depth=self._step_depth)) - - # Run step - self._step_depth += 1 - observation = await step(self.__get_step_params(step)) - self._step_depth -= 1 - - # Add observation to history - self.history.get_current().observation = observation - - # Update its description - step._set_description(await step.describe(self.llm)) - - # Call all subscribed callbacks - self.update_subscribers() - - return observation - - async def run_from_step(self, step: "Step"): - # if self._active: - # raise RuntimeError("Agent is already running") - self._active = True - - next_step = step - is_future_step = False - while not (next_step is None or self._should_halt): - try: - if is_future_step: - # If future step, then we are replaying and need to delete the step from history so it can be replaced - self.history.remove_current_and_substeps() - - observation = await self._run_singular_step(next_step, is_future_step) - if next_step := self.policy.next(self.history): - is_future_step = False - elif next_step := self.history.take_next_step(): - is_future_step = True - else: - next_step = None - - except Exception as e: - print( - f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") - next_step = None - - self._active = False - - # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools - for callback in self._on_update_callbacks: - callback(None) - - async def run_from_observation(self, observation: Observation): - next_step = self.policy.next(self.history) - await self.run_from_step(next_step) - - async def run_policy(self): - first_step = self.policy.next(self.history) - await self.run_from_step(first_step) - - async def _request_halt(self): - if self._active: - self._should_halt = True - while self._active: - time.sleep(0.1) - self._should_halt = False - return None - - async def accept_user_input(self, user_input: str): - self._main_user_input_queue.append(user_input) - self.update_subscribers() - - if len(self._main_user_input_queue) > 1: - return - - # await self._request_halt() - # Just run the step that takes user input, and - # then up to the policy to decide how to deal with it. - self._main_user_input_queue.pop(0) - self.update_subscribers() - await self.run_from_step(UserInputStep(user_input=user_input)) - - while len(self._main_user_input_queue) > 0: - await self.run_from_step(UserInputStep( - user_input=self._main_user_input_queue.pop(0))) - - async def accept_refinement_input(self, user_input: str, index: int): - await self._request_halt() - await self.reverse_to_index(index) - await self.run_from_step(UserInputStep(user_input=user_input)) +class SequentialStep: + pass class Step(ContinueBaseModel): @@ -331,85 +135,6 @@ class Step(ContinueBaseModel): return SequentialStep(steps=steps) -class SequentialStep(Step): - steps: list[Step] - hide: bool = True - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - for step in self.steps: - observation = await sdk.run_step(step) - return observation - - -class ReversibleStep(Step): - async def reverse(self, sdk: ContinueSDK): - raise NotImplementedError - - -class FileSystemEditStep(ReversibleStep): - edit: FileSystemEdit - _diff: Union[EditDiff, None] = None - - hide: bool = True - - async def run(self, sdk: "ContinueSDK") -> Coroutine[Observation, None, None]: - self._diff = await sdk.ide.applyFileSystemEdit(self.edit) - return None - - async def reverse(self, sdk: "ContinueSDK"): - await sdk.ide.applyFileSystemEdit(self._diff.backward) - # Where and when should file saves happen? - - -class ManualEditStep(ReversibleStep): - edit_diff: EditDiff - hide: bool = True - - hide: bool = True - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return "Manual edit step" - # TODO - only handling FileEdit here, but need all other types of FileSystemEdits - # Also requires the merge_file_edit function - # return llm.complete(dedent(f"""This code was replaced: - - # {self.edit_diff.backward.replacement} - - # With this code: - - # {self.edit_diff.forward.replacement} - - # Maximally concise summary of changes in bullet points (can use markdown): - # """)) - - @classmethod - def from_sequence(cls, edits: List[FileEditWithFullContents]) -> "ManualEditStep": - diffs = [] - for edit in edits: - _, diff = FileSystem.apply_edit_to_str( - edit.fileContents, edit.fileEdit) - diffs.append(diff) - return cls(edit_diff=EditDiff.from_sequence(diffs)) - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return None - - async def reverse(self, sdk: ContinueSDK): - await sdk.ide.applyFileSystemEdit(self.edit_diff.backward) - - -class UserInputStep(Step): - user_input: str - name: str = "User Input" - hide: bool = True - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return self.user_input - - async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: - return UserInputObservation(user_input=self.user_input) - - class ValidatorObservation(Observation): passed: bool observation: Observation diff --git a/continuedev/src/continuedev/libs/env.py b/continuedev/src/continuedev/libs/env.py new file mode 100644 index 00000000..d7275b41 --- /dev/null +++ b/continuedev/src/continuedev/libs/env.py @@ -0,0 +1,7 @@ +from dotenv import load_dotenv +import os + +load_dotenv() + + +openai_api_key = os.getenv("OPENAI_API_KEY") diff --git a/continuedev/src/continuedev/libs/sdk.py b/continuedev/src/continuedev/libs/sdk.py new file mode 100644 index 00000000..0295bf35 --- /dev/null +++ b/continuedev/src/continuedev/libs/sdk.py @@ -0,0 +1,39 @@ +from typing import Coroutine, Union +from ..models.filesystem_edit import FileSystemEdit +from .llm import LLM +from .observation import Observation +from ..server.ide_protocol import AbstractIdeProtocolServer +from .core import History, Step +from .steps.core.core import * + + +class Agent: + pass + + +class ContinueSDK: + """The SDK provided as parameters to a step""" + llm: LLM + ide: AbstractIdeProtocolServer + __agent: Agent + + def __init__(self, agent: Agent, llm: Union[LLM, None] = None): + if llm is None: + self.llm = agent.llm + else: + self.llm = llm + self.ide = agent.ide + self.__agent = agent + + @property + def history(self) -> History: + return self.__agent.history + + async def run_step(self, step: Step) -> Coroutine[Observation, None, None]: + return await self.__agent._run_singular_step(step) + + async def apply_filesystem_edit(self, edit: FileSystemEdit): + await self.run_step(FileSystemEditStep(edit=edit)) + + async def wait_for_user_input(self) -> str: + return await self.__agent.wait_for_user_input() diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py new file mode 100644 index 00000000..23086b53 --- /dev/null +++ b/continuedev/src/continuedev/libs/steps/core/core.py @@ -0,0 +1,91 @@ +# These steps are depended upon by ContinueSDK +from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union + +from ....models.filesystem_edit import EditDiff, FileEditWithFullContents, FileSystemEdit +from ....models.filesystem import FileSystem +from ...llm import LLM +from ...observation import Observation, UserInputObservation +from ...core import Step + + +class ContinueSDK: + pass + + +class SequentialStep(Step): + steps: list[Step] + hide: bool = True + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + for step in self.steps: + observation = await sdk.run_step(step) + return observation + + +class ReversibleStep(Step): + async def reverse(self, sdk: ContinueSDK): + raise NotImplementedError + + +class FileSystemEditStep(ReversibleStep): + edit: FileSystemEdit + _diff: Union[EditDiff, None] = None + + hide: bool = True + + async def run(self, sdk: "ContinueSDK") -> Coroutine[Observation, None, None]: + self._diff = await sdk.ide.applyFileSystemEdit(self.edit) + return None + + async def reverse(self, sdk: "ContinueSDK"): + await sdk.ide.applyFileSystemEdit(self._diff.backward) + # Where and when should file saves happen? + + +class ManualEditStep(ReversibleStep): + edit_diff: EditDiff + hide: bool = True + + hide: bool = True + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return "Manual edit step" + # TODO - only handling FileEdit here, but need all other types of FileSystemEdits + # Also requires the merge_file_edit function + # return llm.complete(dedent(f"""This code was replaced: + + # {self.edit_diff.backward.replacement} + + # With this code: + + # {self.edit_diff.forward.replacement} + + # Maximally concise summary of changes in bullet points (can use markdown): + # """)) + + @classmethod + def from_sequence(cls, edits: List[FileEditWithFullContents]) -> "ManualEditStep": + diffs = [] + for edit in edits: + _, diff = FileSystem.apply_edit_to_str( + edit.fileContents, edit.fileEdit) + diffs.append(diff) + return cls(edit_diff=EditDiff.from_sequence(diffs)) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + return None + + async def reverse(self, sdk: ContinueSDK): + await sdk.ide.applyFileSystemEdit(self.edit_diff.backward) + + +class UserInputStep(Step): + user_input: str + name: str = "User Input" + hide: bool = True + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return self.user_input + + async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: + return UserInputObservation(user_input=self.user_input) diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index 70953e95..555c1180 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -51,6 +51,31 @@ class RunCommandStep(Step): return TextObservation(text=stdout) +def ShellCommandsStep(Step): + cmds: List[str] + name: str = "Run Shell Commands" + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return "\n".join(self.cmds) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + cwd = await sdk.ide.getWorkspaceDirectory() + + process = subprocess.Popen( + '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) + + stdin_input = "\n".join(self.cmds) + out, err = process.communicate(stdin_input.encode()) + + # TODO: How to await?? + + # If it fails, return the error + if err is not None and err != "": + return TextObservation(text=err) + + return None + + class WaitForUserInputStep(Step): prompt: str name: str = "Waiting for user input" diff --git a/continuedev/src/continuedev/models/main.py b/continuedev/src/continuedev/models/main.py index 081ec4af..7986b30c 100644 --- a/continuedev/src/continuedev/models/main.py +++ b/continuedev/src/continuedev/models/main.py @@ -4,6 +4,11 @@ from pydantic import BaseModel, root_validator from functools import total_ordering +class ContinueBaseModel(BaseModel): + class Config: + underscore_attrs_are_private = True + + @total_ordering class Position(BaseModel): line: int diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py index c9d4edc5..5eb151d7 100644 --- a/continuedev/src/continuedev/server/notebook.py +++ b/continuedev/src/continuedev/server/notebook.py @@ -6,20 +6,17 @@ from uvicorn.main import Server from ..models.filesystem_edit import FileEditWithFullContents from ..libs.policy import DemoPolicy -from ..libs.core import Agent, FullState, History, Step +from ..libs.core import FullState, History, Step +from ..libs.agent import Agent from ..libs.steps.nate import ImplementAbstractMethodStep from ..libs.observation import Observation -from dotenv import load_dotenv from ..libs.llm.openai import OpenAI from .ide_protocol import AbstractIdeProtocolServer -import os +from ..libs.env import openai_api_key import asyncio import nest_asyncio nest_asyncio.apply() -load_dotenv() -openai_api_key = os.getenv("OPENAI_API_KEY") - router = APIRouter(prefix="/notebook", tags=["notebook"]) # Graceful shutdown by closing websockets -- cgit v1.2.3-70-g09d2 From 657b185c5d40baa99c87feee06e04f0732d85cc0 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 27 May 2023 14:47:33 -0400 Subject: Refactoring and shortcut functions to common steps --- continuedev/src/continuedev/core/agent.py | 180 +++++++++++++++++++++ continuedev/src/continuedev/core/env.py | 7 + continuedev/src/continuedev/core/main.py | 148 +++++++++++++++++ continuedev/src/continuedev/core/observation.py | 35 ++++ continuedev/src/continuedev/core/policy.py | 93 +++++++++++ continuedev/src/continuedev/core/sdk.py | 62 +++++++ continuedev/src/continuedev/libs/agent.py | 180 --------------------- continuedev/src/continuedev/libs/core.py | 148 ----------------- continuedev/src/continuedev/libs/env.py | 7 - continuedev/src/continuedev/libs/observation.py | 35 ---- continuedev/src/continuedev/libs/policy.py | 91 ----------- continuedev/src/continuedev/libs/sdk.py | 39 ----- continuedev/src/continuedev/libs/steps/chroma.py | 9 +- .../src/continuedev/libs/steps/core/core.py | 133 ++++++++++++++- .../libs/steps/draft/abstract_method.py | 3 +- .../src/continuedev/libs/steps/draft/dlt.py | 3 +- .../src/continuedev/libs/steps/draft/redux.py | 7 +- .../src/continuedev/libs/steps/draft/typeorm.py | 3 +- continuedev/src/continuedev/libs/steps/main.py | 174 +++----------------- .../src/continuedev/libs/steps/migration.py | 2 +- continuedev/src/continuedev/libs/steps/nate.py | 9 +- continuedev/src/continuedev/libs/steps/pytest.py | 2 +- continuedev/src/continuedev/libs/steps/ty.py | 8 +- .../continuedev/plugins/policy/libs/alternate.py | 4 +- .../src/continuedev/plugins/step/hookspecs.py | 4 +- continuedev/src/continuedev/server/notebook.py | 10 +- 26 files changed, 713 insertions(+), 683 deletions(-) create mode 100644 continuedev/src/continuedev/core/agent.py create mode 100644 continuedev/src/continuedev/core/env.py create mode 100644 continuedev/src/continuedev/core/main.py create mode 100644 continuedev/src/continuedev/core/observation.py create mode 100644 continuedev/src/continuedev/core/policy.py create mode 100644 continuedev/src/continuedev/core/sdk.py delete mode 100644 continuedev/src/continuedev/libs/agent.py delete mode 100644 continuedev/src/continuedev/libs/core.py delete mode 100644 continuedev/src/continuedev/libs/env.py delete mode 100644 continuedev/src/continuedev/libs/observation.py delete mode 100644 continuedev/src/continuedev/libs/policy.py delete mode 100644 continuedev/src/continuedev/libs/sdk.py (limited to 'continuedev') diff --git a/continuedev/src/continuedev/core/agent.py b/continuedev/src/continuedev/core/agent.py new file mode 100644 index 00000000..509a54b3 --- /dev/null +++ b/continuedev/src/continuedev/core/agent.py @@ -0,0 +1,180 @@ +import traceback +import time +from typing import Callable, Coroutine, List +from ..models.filesystem_edit import FileEditWithFullContents +from ..libs.llm import LLM +from .observation import Observation +from ..server.ide_protocol import AbstractIdeProtocolServer +from ..libs.util.queue import AsyncSubscriptionQueue +from ..models.main import ContinueBaseModel +from .main import Policy, History, FullState, Step, HistoryNode +from ..libs.steps.core.core import ReversibleStep, ManualEditStep, UserInputStep +from .sdk import ContinueSDK + + +class Agent(ContinueBaseModel): + llm: LLM + policy: Policy + ide: AbstractIdeProtocolServer + history: History = History.from_empty() + continue_sdk: "ContinueSDK" + _on_update_callbacks: List[Callable[[FullState], None]] = [] + + _active: bool = False + _should_halt: bool = False + _main_user_input_queue: List[str] = [] + + _user_input_queue = AsyncSubscriptionQueue() + + class Config: + arbitrary_types_allowed = True + + def get_full_state(self) -> FullState: + return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) + + def on_update(self, callback: Callable[["FullState"], None]): + """Subscribe to changes to state""" + self._on_update_callbacks.append(callback) + + def update_subscribers(self): + full_state = self.get_full_state() + for callback in self._on_update_callbacks: + callback(full_state) + + def __get_step_params(self, step: "Step"): + return ContinueSDK(agent=self, llm=self.llm.with_system_message(step.system_message)) + + def give_user_input(self, input: str, index: int): + self._user_input_queue.post(index, input) + + async def wait_for_user_input(self) -> str: + self._active = False + self.update_subscribers() + await self._user_input_queue.get(self.history.current_index) + self._active = True + self.update_subscribers() + + _manual_edits_buffer: List[FileEditWithFullContents] = [] + + async def reverse_to_index(self, index: int): + try: + while self.history.get_current_index() >= index: + current_step = self.history.get_current().step + self.history.step_back() + if issubclass(current_step.__class__, ReversibleStep): + await current_step.reverse(self.__get_step_params(current_step)) + + self.update_subscribers() + except Exception as e: + print(e) + + def handle_manual_edits(self, edits: List[FileEditWithFullContents]): + for edit in edits: + 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 + + _step_depth: int = 0 + + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: + if not is_future_step: + # Check manual edits buffer, clear out if needed by creating a ManualEditStep + if len(self._manual_edits_buffer) > 0: + manualEditsStep = ManualEditStep.from_sequence( + self._manual_edits_buffer) + self._manual_edits_buffer = [] + await self._run_singular_step(manualEditsStep) + + # Update history - do this first so we get top-first tree ordering + self.history.add_node(HistoryNode( + step=step, observation=None, depth=self._step_depth)) + + # Run step + self._step_depth += 1 + observation = await step(self.__get_step_params(step)) + self._step_depth -= 1 + + # Add observation to history + self.history.get_current().observation = observation + + # Update its description + step._set_description(await step.describe(self.llm)) + + # Call all subscribed callbacks + self.update_subscribers() + + return observation + + async def run_from_step(self, step: "Step"): + # if self._active: + # raise RuntimeError("Agent is already running") + self._active = True + + next_step = step + is_future_step = False + while not (next_step is None or self._should_halt): + try: + if is_future_step: + # If future step, then we are replaying and need to delete the step from history so it can be replaced + self.history.remove_current_and_substeps() + + observation = await self._run_singular_step(next_step, is_future_step) + if next_step := self.policy.next(self.history): + is_future_step = False + elif next_step := self.history.take_next_step(): + is_future_step = True + else: + next_step = None + + except Exception as e: + print( + f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") + next_step = None + + self._active = False + + # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools + for callback in self._on_update_callbacks: + callback(None) + + async def run_from_observation(self, observation: Observation): + next_step = self.policy.next(self.history) + await self.run_from_step(next_step) + + async def run_policy(self): + first_step = self.policy.next(self.history) + await self.run_from_step(first_step) + + async def _request_halt(self): + if self._active: + self._should_halt = True + while self._active: + time.sleep(0.1) + self._should_halt = False + return None + + async def accept_user_input(self, user_input: str): + self._main_user_input_queue.append(user_input) + self.update_subscribers() + + if len(self._main_user_input_queue) > 1: + return + + # await self._request_halt() + # Just run the step that takes user input, and + # then up to the policy to decide how to deal with it. + self._main_user_input_queue.pop(0) + self.update_subscribers() + await self.run_from_step(UserInputStep(user_input=user_input)) + + while len(self._main_user_input_queue) > 0: + await self.run_from_step(UserInputStep( + user_input=self._main_user_input_queue.pop(0))) + + async def accept_refinement_input(self, user_input: str, index: int): + await self._request_halt() + await self.reverse_to_index(index) + await self.run_from_step(UserInputStep(user_input=user_input)) diff --git a/continuedev/src/continuedev/core/env.py b/continuedev/src/continuedev/core/env.py new file mode 100644 index 00000000..d7275b41 --- /dev/null +++ b/continuedev/src/continuedev/core/env.py @@ -0,0 +1,7 @@ +from dotenv import load_dotenv +import os + +load_dotenv() + + +openai_api_key = os.getenv("OPENAI_API_KEY") diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py new file mode 100644 index 00000000..51fcd299 --- /dev/null +++ b/continuedev/src/continuedev/core/main.py @@ -0,0 +1,148 @@ +from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union + +from ..models.main import ContinueBaseModel +from pydantic import validator +from ..libs.llm import LLM +from .observation import Observation + + +class HistoryNode(ContinueBaseModel): + """A point in history, a list of which make up History""" + step: "Step" + observation: Union[Observation, None] + depth: int + + +class History(ContinueBaseModel): + """A history of steps taken and their results""" + timeline: List[HistoryNode] + current_index: int + + def add_node(self, node: HistoryNode): + self.timeline.insert(self.current_index + 1, node) + self.current_index += 1 + + def get_current(self) -> Union[HistoryNode, None]: + if self.current_index < 0: + return None + return self.timeline[self.current_index] + + def remove_current_and_substeps(self): + self.timeline.pop(self.current_index) + while self.get_current() is not None and self.get_current().depth > 0: + self.timeline.pop(self.current_index) + + def take_next_step(self) -> Union["Step", None]: + if self.has_future(): + self.current_index += 1 + current_state = self.get_current() + if current_state is None: + return None + return current_state.step + return None + + def get_current_index(self) -> int: + return self.current_index + + def has_future(self) -> bool: + return self.current_index < len(self.timeline) - 1 + + def step_back(self): + self.current_index -= 1 + + def last_observation(self) -> Union[Observation, None]: + state = self.get_current() + if state is None: + return None + return state.observation + + @classmethod + def from_empty(cls): + return cls(timeline=[], current_index=-1) + + +class FullState(ContinueBaseModel): + """A full state of the program, including the history""" + history: History + active: bool + user_input_queue: List[str] + + +class ContinueSDK: + pass + + +class SequentialStep: + pass + + +class Policy(ContinueBaseModel): + """A rule that determines which step to take next""" + + # Note that history is mutable, kinda sus + def next(self, history: History = History.from_empty()) -> "Step": + raise NotImplementedError + + +class Step(ContinueBaseModel): + name: str = None + hide: bool = False + _description: Union[str, None] = None + + system_message: Union[str, None] = None + + class Config: + copy_on_model_validation = False + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + if self._description is not None: + return self._description + return "Running step: " + self.name + + def _set_description(self, description: str): + self._description = description + + def dict(self, *args, **kwargs): + d = super().dict(*args, **kwargs) + if self._description is not None: + d["description"] = self._description + else: + d["description"] = self.name + return d + + @validator("name", pre=True, always=True) + def name_is_class_name(cls, name): + if name is None: + return cls.__name__ + return name + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + raise NotImplementedError + + async def __call__(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + return await self.run(sdk) + + def __rshift__(self, other: "Step"): + steps = [] + if isinstance(self, SequentialStep): + steps = self.steps + else: + steps.append(self) + if isinstance(other, SequentialStep): + steps += other.steps + else: + steps.append(other) + return SequentialStep(steps=steps) + + +class ValidatorObservation(Observation): + passed: bool + observation: Observation + + +class Validator(Step): + def run(self, sdk: ContinueSDK) -> ValidatorObservation: + raise NotImplementedError + + +HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/core/observation.py b/continuedev/src/continuedev/core/observation.py new file mode 100644 index 00000000..fef04311 --- /dev/null +++ b/continuedev/src/continuedev/core/observation.py @@ -0,0 +1,35 @@ +from pydantic import BaseModel, validator +from ..models.main import Traceback + + +class Observation(BaseModel): + pass + + +class TracebackObservation(Observation): + traceback: Traceback + + +class ValidatorObservation(Observation): + passed: bool + + +class UserInputObservation(Observation): + user_input: str + + +class DictObservation(Observation): + values: dict + + def __getitem__(self, key): + return self.values[key] + + +class TextObservation(Observation): + text: str + + @validator("text", pre=True, always=True) + def text_not_none(cls, v): + if v is None: + return "" + return v diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py new file mode 100644 index 00000000..9e6abf14 --- /dev/null +++ b/continuedev/src/continuedev/core/policy.py @@ -0,0 +1,93 @@ +from typing import List, Tuple, Type + +from ..models.main import ContinueBaseModel + +from ..libs.steps.ty import CreatePipelineStep +from .main import Step, Validator, History, Policy +from .observation import Observation, TracebackObservation, UserInputObservation +from ..libs.steps.main import EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep +from ..libs.steps.nate import WritePytestsStep, CreateTableStep +from ..libs.steps.chroma import AnswerQuestionChroma, EditFileChroma + + +class DemoPolicy(Policy): + ran_code_last: bool = False + cmd: str + + def next(self, history: History) -> Step: + observation = history.last_observation() + if observation is not None and isinstance(observation, UserInputObservation): + # This could be defined with ObservationTypePolicy. Ergonomics not right though. + if " test" in observation.user_input.lower(): + return WritePytestsStep(instructions=observation.user_input) + elif "/dlt" in observation.user_input.lower() or " dlt" in observation.user_input.lower(): + return CreatePipelineStep() + elif "/table" in observation.user_input: + return CreateTableStep(sql_str=" ".join(observation.user_input.split(" ")[1:])) + elif "/ask" in observation.user_input: + return AnswerQuestionChroma(question=" ".join(observation.user_input.split(" ")[1:])) + elif "/edit" in observation.user_input: + return EditFileChroma(request=" ".join(observation.user_input.split(" ")[1:])) + return EditHighlightedCodeStep(user_input=observation.user_input) + + state = history.get_current() + if state is None or not self.ran_code_last: + self.ran_code_last = True + return RunCodeStep(cmd=self.cmd) + + if observation is not None and isinstance(observation, TracebackObservation): + self.ran_code_last = False + return SolveTracebackStep(traceback=observation.traceback) + else: + return None + + +class ObservationTypePolicy(Policy): + def __init__(self, base_policy: Policy, observation_type: Type[Observation], step_type: Type[Step]): + self.observation_type = observation_type + self.step_type = step_type + self.base_policy = base_policy + + def next(self, history: History) -> Step: + observation = history.last_observation() + if observation is not None and isinstance(observation, self.observation_type): + return self.step_type(observation) + return self.base_policy.next(history) + + +class PolicyWrappedWithValidators(Policy): + """Default is to stop, unless the validator tells what to do next""" + index: int + stage: int + + def __init__(self, base_policy: Policy, pairs: List[Tuple[Validator, Type[Step]]]): + # Want to pass Type[Validator], or just the Validator? Question of where params are coming from. + self.pairs = pairs + self.index = len(pairs) + self.validating = 0 + self.base_policy = base_policy + + def next(self, history: History) -> Step: + if self.index == len(self.pairs): + self.index = 0 + return self.base_policy.next(history) + + if self.stage == 0: + # Running the validator at the current index for the first time + validator, step = self.pairs[self.index] + self.stage = 1 + return validator + elif self.stage == 1: + # Previously ran the validator at the current index, now receiving its ValidatorObservation + observation = history.last_observation() + if observation.passed: + self.stage = 0 + self.index += 1 + if self.index == len(self.pairs): + self.index = 0 + return self.base_policy.next(history) + else: + return self.pairs[self.index][0] + else: + _, step_type = self.pairs[self.index] + return step_type(observation) diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py new file mode 100644 index 00000000..ff62a2b1 --- /dev/null +++ b/continuedev/src/continuedev/core/sdk.py @@ -0,0 +1,62 @@ +from typing import Coroutine, Union +from ..models.filesystem_edit import FileSystemEdit +from ..models.filesystem import RangeInFile +from ..libs.llm import LLM +from .observation import Observation +from ..server.ide_protocol import AbstractIdeProtocolServer +from .main import History, Step +from ..libs.steps.core.core import * + + +class Agent: + pass + + +class ContinueSDKSteps: + def __init__(self, sdk: "ContinueSDK"): + self.sdk = sdk + + +class ContinueSDK: + """The SDK provided as parameters to a step""" + llm: LLM + ide: AbstractIdeProtocolServer + steps: ContinueSDKSteps + __agent: Agent + + def __init__(self, agent: Agent, llm: Union[LLM, None] = None): + if llm is None: + self.llm = agent.llm + else: + self.llm = llm + self.ide = agent.ide + self.__agent = agent + self.steps = ContinueSDKSteps(self) + + @property + def history(self) -> History: + return self.__agent.history + + async def run_step(self, step: Step) -> Coroutine[Observation, None, None]: + return await self.__agent._run_singular_step(step) + + async def apply_filesystem_edit(self, edit: FileSystemEdit): + await self.run_step(FileSystemEditStep(edit=edit)) + + async def wait_for_user_input(self) -> str: + return await self.__agent.wait_for_user_input() + + async def wait_for_user_confirmation(self, prompt: str): + return await self.run_step(WaitForUserConfirmationStep(prompt=prompt)) + + async def run(self, commands: List[str] | str, cwd: str = None): + commands = commands if isinstance(commands, List) else [commands] + return self.run_step(ShellCommandsStep(commands=commands, cwd=cwd)) + + async def edit_file(self, filename: str, prompt: str): + await self.ide.setFileOpen(filename) + contents = await self.ide.readFile(filename) + await self.run_step(EditCodeStep( + range_in_files=[RangeInFile.from_entire_file(filename, contents)], + prompt=f'Here is the code before:\n\n{{code}}\n\nHere is the user request:\n\n{prompt}\n\nHere is the code edited to perfectly solve the user request:\n\n' + )) diff --git a/continuedev/src/continuedev/libs/agent.py b/continuedev/src/continuedev/libs/agent.py deleted file mode 100644 index 466aa234..00000000 --- a/continuedev/src/continuedev/libs/agent.py +++ /dev/null @@ -1,180 +0,0 @@ -import traceback -import time -from typing import Callable, Coroutine, List -from ..models.filesystem_edit import FileEditWithFullContents -from .llm import LLM -from .observation import Observation -from ..server.ide_protocol import AbstractIdeProtocolServer -from .util.queue import AsyncSubscriptionQueue -from ..models.main import ContinueBaseModel -from .core import Policy, History, FullState, Step, HistoryNode -from .steps.core.core import ReversibleStep, ManualEditStep, UserInputStep -from .sdk import ContinueSDK - - -class Agent(ContinueBaseModel): - llm: LLM - policy: Policy - ide: AbstractIdeProtocolServer - history: History = History.from_empty() - continue_sdk: "ContinueSDK" - _on_update_callbacks: List[Callable[[FullState], None]] = [] - - _active: bool = False - _should_halt: bool = False - _main_user_input_queue: List[str] = [] - - _user_input_queue = AsyncSubscriptionQueue() - - class Config: - arbitrary_types_allowed = True - - def get_full_state(self) -> FullState: - return FullState(history=self.history, active=self._active, user_input_queue=self._main_user_input_queue) - - def on_update(self, callback: Callable[["FullState"], None]): - """Subscribe to changes to state""" - self._on_update_callbacks.append(callback) - - def update_subscribers(self): - full_state = self.get_full_state() - for callback in self._on_update_callbacks: - callback(full_state) - - def __get_step_params(self, step: "Step"): - return ContinueSDK(agent=self, llm=self.llm.with_system_message(step.system_message)) - - def give_user_input(self, input: str, index: int): - self._user_input_queue.post(index, input) - - async def wait_for_user_input(self) -> str: - self._active = False - self.update_subscribers() - await self._user_input_queue.get(self.history.current_index) - self._active = True - self.update_subscribers() - - _manual_edits_buffer: List[FileEditWithFullContents] = [] - - async def reverse_to_index(self, index: int): - try: - while self.history.get_current_index() >= index: - current_step = self.history.get_current().step - self.history.step_back() - if issubclass(current_step.__class__, ReversibleStep): - await current_step.reverse(self.__get_step_params(current_step)) - - self.update_subscribers() - except Exception as e: - print(e) - - def handle_manual_edits(self, edits: List[FileEditWithFullContents]): - for edit in edits: - 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 - - _step_depth: int = 0 - - async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: - if not is_future_step: - # Check manual edits buffer, clear out if needed by creating a ManualEditStep - if len(self._manual_edits_buffer) > 0: - manualEditsStep = ManualEditStep.from_sequence( - self._manual_edits_buffer) - self._manual_edits_buffer = [] - await self._run_singular_step(manualEditsStep) - - # Update history - do this first so we get top-first tree ordering - self.history.add_node(HistoryNode( - step=step, observation=None, depth=self._step_depth)) - - # Run step - self._step_depth += 1 - observation = await step(self.__get_step_params(step)) - self._step_depth -= 1 - - # Add observation to history - self.history.get_current().observation = observation - - # Update its description - step._set_description(await step.describe(self.llm)) - - # Call all subscribed callbacks - self.update_subscribers() - - return observation - - async def run_from_step(self, step: "Step"): - # if self._active: - # raise RuntimeError("Agent is already running") - self._active = True - - next_step = step - is_future_step = False - while not (next_step is None or self._should_halt): - try: - if is_future_step: - # If future step, then we are replaying and need to delete the step from history so it can be replaced - self.history.remove_current_and_substeps() - - observation = await self._run_singular_step(next_step, is_future_step) - if next_step := self.policy.next(self.history): - is_future_step = False - elif next_step := self.history.take_next_step(): - is_future_step = True - else: - next_step = None - - except Exception as e: - print( - f"Error while running step: \n{''.join(traceback.format_tb(e.__traceback__))}\n{e}") - next_step = None - - self._active = False - - # Doing this so active can make it to the frontend after steps are done. But want better state syncing tools - for callback in self._on_update_callbacks: - callback(None) - - async def run_from_observation(self, observation: Observation): - next_step = self.policy.next(self.history) - await self.run_from_step(next_step) - - async def run_policy(self): - first_step = self.policy.next(self.history) - await self.run_from_step(first_step) - - async def _request_halt(self): - if self._active: - self._should_halt = True - while self._active: - time.sleep(0.1) - self._should_halt = False - return None - - async def accept_user_input(self, user_input: str): - self._main_user_input_queue.append(user_input) - self.update_subscribers() - - if len(self._main_user_input_queue) > 1: - return - - # await self._request_halt() - # Just run the step that takes user input, and - # then up to the policy to decide how to deal with it. - self._main_user_input_queue.pop(0) - self.update_subscribers() - await self.run_from_step(UserInputStep(user_input=user_input)) - - while len(self._main_user_input_queue) > 0: - await self.run_from_step(UserInputStep( - user_input=self._main_user_input_queue.pop(0))) - - async def accept_refinement_input(self, user_input: str, index: int): - await self._request_halt() - await self.reverse_to_index(index) - await self.run_from_step(UserInputStep(user_input=user_input)) diff --git a/continuedev/src/continuedev/libs/core.py b/continuedev/src/continuedev/libs/core.py deleted file mode 100644 index 9d432c4b..00000000 --- a/continuedev/src/continuedev/libs/core.py +++ /dev/null @@ -1,148 +0,0 @@ -from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union - -from ..models.main import ContinueBaseModel -from pydantic import validator -from .llm import LLM -from .observation import Observation - - -class HistoryNode(ContinueBaseModel): - """A point in history, a list of which make up History""" - step: "Step" - observation: Union[Observation, None] - depth: int - - -class History(ContinueBaseModel): - """A history of steps taken and their results""" - timeline: List[HistoryNode] - current_index: int - - def add_node(self, node: HistoryNode): - self.timeline.insert(self.current_index + 1, node) - self.current_index += 1 - - def get_current(self) -> Union[HistoryNode, None]: - if self.current_index < 0: - return None - return self.timeline[self.current_index] - - def remove_current_and_substeps(self): - self.timeline.pop(self.current_index) - while self.get_current() is not None and self.get_current().depth > 0: - self.timeline.pop(self.current_index) - - def take_next_step(self) -> Union["Step", None]: - if self.has_future(): - self.current_index += 1 - current_state = self.get_current() - if current_state is None: - return None - return current_state.step - return None - - def get_current_index(self) -> int: - return self.current_index - - def has_future(self) -> bool: - return self.current_index < len(self.timeline) - 1 - - def step_back(self): - self.current_index -= 1 - - def last_observation(self) -> Union[Observation, None]: - state = self.get_current() - if state is None: - return None - return state.observation - - @classmethod - def from_empty(cls): - return cls(timeline=[], current_index=-1) - - -class FullState(ContinueBaseModel): - """A full state of the program, including the history""" - history: History - active: bool - user_input_queue: List[str] - - -class Policy(ContinueBaseModel): - """A rule that determines which step to take next""" - - # Note that history is mutable, kinda sus - def next(self, history: History = History.from_empty()) -> "Step": - raise NotImplementedError - - -class ContinueSDK: - pass - - -class SequentialStep: - pass - - -class Step(ContinueBaseModel): - name: str = None - hide: bool = False - _description: Union[str, None] = None - - system_message: Union[str, None] = None - - class Config: - copy_on_model_validation = False - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - if self._description is not None: - return self._description - return "Running step: " + self.name - - def _set_description(self, description: str): - self._description = description - - def dict(self, *args, **kwargs): - d = super().dict(*args, **kwargs) - if self._description is not None: - d["description"] = self._description - else: - d["description"] = self.name - return d - - @validator("name", pre=True, always=True) - def name_is_class_name(cls, name): - if name is None: - return cls.__name__ - return name - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - raise NotImplementedError - - async def __call__(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - return await self.run(sdk) - - def __rshift__(self, other: "Step"): - steps = [] - if isinstance(self, SequentialStep): - steps = self.steps - else: - steps.append(self) - if isinstance(other, SequentialStep): - steps += other.steps - else: - steps.append(other) - return SequentialStep(steps=steps) - - -class ValidatorObservation(Observation): - passed: bool - observation: Observation - - -class Validator(Step): - def run(self, sdk: ContinueSDK) -> ValidatorObservation: - raise NotImplementedError - - -HistoryNode.update_forward_refs() diff --git a/continuedev/src/continuedev/libs/env.py b/continuedev/src/continuedev/libs/env.py deleted file mode 100644 index d7275b41..00000000 --- a/continuedev/src/continuedev/libs/env.py +++ /dev/null @@ -1,7 +0,0 @@ -from dotenv import load_dotenv -import os - -load_dotenv() - - -openai_api_key = os.getenv("OPENAI_API_KEY") diff --git a/continuedev/src/continuedev/libs/observation.py b/continuedev/src/continuedev/libs/observation.py deleted file mode 100644 index fef04311..00000000 --- a/continuedev/src/continuedev/libs/observation.py +++ /dev/null @@ -1,35 +0,0 @@ -from pydantic import BaseModel, validator -from ..models.main import Traceback - - -class Observation(BaseModel): - pass - - -class TracebackObservation(Observation): - traceback: Traceback - - -class ValidatorObservation(Observation): - passed: bool - - -class UserInputObservation(Observation): - user_input: str - - -class DictObservation(Observation): - values: dict - - def __getitem__(self, key): - return self.values[key] - - -class TextObservation(Observation): - text: str - - @validator("text", pre=True, always=True) - def text_not_none(cls, v): - if v is None: - return "" - return v diff --git a/continuedev/src/continuedev/libs/policy.py b/continuedev/src/continuedev/libs/policy.py deleted file mode 100644 index 586eaebe..00000000 --- a/continuedev/src/continuedev/libs/policy.py +++ /dev/null @@ -1,91 +0,0 @@ -from typing import List, Tuple, Type - -from .steps.ty import CreatePipelineStep -from .core import Step, Validator, Policy, History -from .observation import Observation, TracebackObservation, UserInputObservation -from .steps.main import EditCodeStep, EditHighlightedCodeStep, SolveTracebackStep, RunCodeStep, FasterEditHighlightedCodeStep -from .steps.nate import WritePytestsStep, CreateTableStep -from .steps.chroma import AnswerQuestionChroma, EditFileChroma - - -class DemoPolicy(Policy): - ran_code_last: bool = False - cmd: str - - def next(self, history: History) -> Step: - observation = history.last_observation() - if observation is not None and isinstance(observation, UserInputObservation): - # This could be defined with ObservationTypePolicy. Ergonomics not right though. - if " test" in observation.user_input.lower(): - return WritePytestsStep(instructions=observation.user_input) - elif "/dlt" in observation.user_input.lower() or " dlt" in observation.user_input.lower(): - return CreatePipelineStep() - elif "/table" in observation.user_input: - return CreateTableStep(sql_str=" ".join(observation.user_input.split(" ")[1:])) - elif "/ask" in observation.user_input: - return AnswerQuestionChroma(question=" ".join(observation.user_input.split(" ")[1:])) - elif "/edit" in observation.user_input: - return EditFileChroma(request=" ".join(observation.user_input.split(" ")[1:])) - return EditHighlightedCodeStep(user_input=observation.user_input) - - state = history.get_current() - if state is None or not self.ran_code_last: - self.ran_code_last = True - return RunCodeStep(cmd=self.cmd) - - if observation is not None and isinstance(observation, TracebackObservation): - self.ran_code_last = False - return SolveTracebackStep(traceback=observation.traceback) - else: - return None - - -class ObservationTypePolicy(Policy): - def __init__(self, base_policy: Policy, observation_type: Type[Observation], step_type: Type[Step]): - self.observation_type = observation_type - self.step_type = step_type - self.base_policy = base_policy - - def next(self, history: History) -> Step: - observation = history.last_observation() - if observation is not None and isinstance(observation, self.observation_type): - return self.step_type(observation) - return self.base_policy.next(history) - - -class PolicyWrappedWithValidators(Policy): - """Default is to stop, unless the validator tells what to do next""" - index: int - stage: int - - def __init__(self, base_policy: Policy, pairs: List[Tuple[Validator, Type[Step]]]): - # Want to pass Type[Validator], or just the Validator? Question of where params are coming from. - self.pairs = pairs - self.index = len(pairs) - self.validating = 0 - self.base_policy = base_policy - - def next(self, history: History) -> Step: - if self.index == len(self.pairs): - self.index = 0 - return self.base_policy.next(history) - - if self.stage == 0: - # Running the validator at the current index for the first time - validator, step = self.pairs[self.index] - self.stage = 1 - return validator - elif self.stage == 1: - # Previously ran the validator at the current index, now receiving its ValidatorObservation - observation = history.last_observation() - if observation.passed: - self.stage = 0 - self.index += 1 - if self.index == len(self.pairs): - self.index = 0 - return self.base_policy.next(history) - else: - return self.pairs[self.index][0] - else: - _, step_type = self.pairs[self.index] - return step_type(observation) diff --git a/continuedev/src/continuedev/libs/sdk.py b/continuedev/src/continuedev/libs/sdk.py deleted file mode 100644 index 0295bf35..00000000 --- a/continuedev/src/continuedev/libs/sdk.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Coroutine, Union -from ..models.filesystem_edit import FileSystemEdit -from .llm import LLM -from .observation import Observation -from ..server.ide_protocol import AbstractIdeProtocolServer -from .core import History, Step -from .steps.core.core import * - - -class Agent: - pass - - -class ContinueSDK: - """The SDK provided as parameters to a step""" - llm: LLM - ide: AbstractIdeProtocolServer - __agent: Agent - - def __init__(self, agent: Agent, llm: Union[LLM, None] = None): - if llm is None: - self.llm = agent.llm - else: - self.llm = llm - self.ide = agent.ide - self.__agent = agent - - @property - def history(self) -> History: - return self.__agent.history - - async def run_step(self, step: Step) -> Coroutine[Observation, None, None]: - return await self.__agent._run_singular_step(step) - - async def apply_filesystem_edit(self, edit: FileSystemEdit): - await self.run_step(FileSystemEditStep(edit=edit)) - - async def wait_for_user_input(self) -> str: - return await self.__agent.wait_for_user_input() diff --git a/continuedev/src/continuedev/libs/steps/chroma.py b/continuedev/src/continuedev/libs/steps/chroma.py index 2d8742e8..f13a2bab 100644 --- a/continuedev/src/continuedev/libs/steps/chroma.py +++ b/continuedev/src/continuedev/libs/steps/chroma.py @@ -1,11 +1,10 @@ from textwrap import dedent from typing import Coroutine, Union -from ...models.filesystem_edit import AddDirectory, AddFile -from ..observation import Observation, TextObservation -from ..core import Step, ContinueSDK -from .main import EditCodeStep, EditFileStep, RunCommandStep, WaitForUserConfirmationStep +from ...core.observation import Observation, TextObservation +from ...core.main import Step, ContinueSDK +from .core.core import EditFileStep from ..chroma.query import query_codebase_index -from .main import EditFileStep +from .core.core import EditFileStep class AnswerQuestionChroma(Step): diff --git a/continuedev/src/continuedev/libs/steps/core/core.py b/continuedev/src/continuedev/libs/steps/core/core.py index 23086b53..0338d635 100644 --- a/continuedev/src/continuedev/libs/steps/core/core.py +++ b/continuedev/src/continuedev/libs/steps/core/core.py @@ -1,11 +1,16 @@ # These steps are depended upon by ContinueSDK -from typing import Callable, Coroutine, Dict, Generator, List, Tuple, Union +import subprocess +from textwrap import dedent +from typing import Coroutine, List, Union +from ...llm.prompt_utils import MarkdownStyleEncoderDecoder + +from ...util.traceback_parsers import parse_python_traceback from ....models.filesystem_edit import EditDiff, FileEditWithFullContents, FileSystemEdit -from ....models.filesystem import FileSystem +from ....models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...llm import LLM -from ...observation import Observation, UserInputObservation -from ...core import Step +from ....core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation +from ....core.main import Step class ContinueSDK: @@ -42,6 +47,98 @@ class FileSystemEditStep(ReversibleStep): # Where and when should file saves happen? +def ShellCommandsStep(Step): + cmds: List[str] + cwd: str | None = None + name: str = "Run Shell Commands" + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return "\n".join(self.cmds) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd + + process = subprocess.Popen( + '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) + + stdin_input = "\n".join(self.cmds) + out, err = process.communicate(stdin_input.encode()) + + # If it fails, return the error + if err is not None and err != "": + return TextObservation(text=err) + + return None + + +class EditCodeStep(Step): + # Might make an even more specific atomic step, which is "apply file edit" + range_in_files: List[RangeInFile] + prompt: str # String with {code} somewhere + name: str = "Edit code" + + _edit_diffs: Union[List[EditDiff], None] = None + _prompt: Union[str, None] = None + _completion: Union[str, None] = None + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + if self._edit_diffs is None: + return "Editing files: " + ", ".join(map(lambda rif: rif.filepath, self.range_in_files)) + elif len(self._edit_diffs) == 0: + return "No edits made" + else: + return llm.complete(dedent(f"""{self._prompt}{self._completion} + + Maximally concise summary of changes in bullet points (can use markdown): + """)) + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + rif_with_contents = [] + for range_in_file in self.range_in_files: + file_contents = await sdk.ide.readRangeInFile(range_in_file) + rif_with_contents.append( + RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) + enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) + code_string = enc_dec.encode() + prompt = self.prompt.format(code=code_string) + + completion = sdk.llm.complete(prompt) + + # Temporarily doing this to generate description. + self._prompt = prompt + self._completion = completion + + file_edits = enc_dec.decode(completion) + + self._edit_diffs = [] + for file_edit in file_edits: + diff = await sdk.apply_filesystem_edit(file_edit) + self._edit_diffs.append(diff) + + for filepath in set([file_edit.filepath for file_edit in file_edits]): + await sdk.ide.saveFile(filepath) + await sdk.ide.setFileOpen(filepath) + + return None + + +class EditFileStep(Step): + filepath: str + prompt: str + hide: bool = True + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return "Editing file: " + self.filepath + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + file_contents = await sdk.ide.readFile(self.filepath) + await sdk.run_step(EditCodeStep( + range_in_files=[RangeInFile.from_entire_file( + self.filepath, file_contents)], + prompt=self.prompt + )) + + class ManualEditStep(ReversibleStep): edit_diff: EditDiff hide: bool = True @@ -89,3 +186,31 @@ class UserInputStep(Step): async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]: return UserInputObservation(user_input=self.user_input) + + +class WaitForUserInputStep(Step): + prompt: str + name: str = "Waiting for user input" + + _description: Union[str, None] = None + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return self.prompt + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + self._description = self.prompt + resp = await sdk.wait_for_user_input() + return TextObservation(text=resp) + + +class WaitForUserConfirmationStep(Step): + prompt: str + name: str = "Waiting for user confirmation" + + async def describe(self, llm: LLM) -> Coroutine[str, None, None]: + return self.prompt + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + self._description = self.prompt + resp = await sdk.wait_for_user_input() + return TextObservation(text=resp) diff --git a/continuedev/src/continuedev/libs/steps/draft/abstract_method.py b/continuedev/src/continuedev/libs/steps/draft/abstract_method.py index 927d93fd..f3131c4b 100644 --- a/continuedev/src/continuedev/libs/steps/draft/abstract_method.py +++ b/continuedev/src/continuedev/libs/steps/draft/abstract_method.py @@ -1,4 +1,5 @@ -from ...core import ContinueSDK, Step +from ....core.sdk import ContinueSDK +from ....core.main import Step class ImplementAbstractMethodStep(Step): diff --git a/continuedev/src/continuedev/libs/steps/draft/dlt.py b/continuedev/src/continuedev/libs/steps/draft/dlt.py index 608f089a..5ba5692a 100644 --- a/continuedev/src/continuedev/libs/steps/draft/dlt.py +++ b/continuedev/src/continuedev/libs/steps/draft/dlt.py @@ -1,6 +1,7 @@ from textwrap import dedent from ....models.filesystem_edit import AddFile -from ...core import Step, ContinueSDK +from ....core.main import Step +from ....core.sdk import ContinueSDK from ..main import WaitForUserInputStep diff --git a/continuedev/src/continuedev/libs/steps/draft/redux.py b/continuedev/src/continuedev/libs/steps/draft/redux.py index 52a8fbd8..efaa9ba4 100644 --- a/continuedev/src/continuedev/libs/steps/draft/redux.py +++ b/continuedev/src/continuedev/libs/steps/draft/redux.py @@ -1,7 +1,6 @@ -from textwrap import dedent -from ....models.filesystem_edit import AddFile -from ...core import Step, ContinueSDK -from ..main import WaitForUserInputStep, EditFileStep +from ....core.main import Step +from ....core.sdk import ContinueSDK +from ..core.core import EditFileStep class EditReduxStateStep(Step): diff --git a/continuedev/src/continuedev/libs/steps/draft/typeorm.py b/continuedev/src/continuedev/libs/steps/draft/typeorm.py index 9d058f1e..d06a6fb4 100644 --- a/continuedev/src/continuedev/libs/steps/draft/typeorm.py +++ b/continuedev/src/continuedev/libs/steps/draft/typeorm.py @@ -1,5 +1,6 @@ from textwrap import dedent -from ...core import Step, ContinueSDK +from ....core.main import Step +from ....core.sdk import ContinueSDK class CreateTableStep(Step): diff --git a/continuedev/src/continuedev/libs/steps/main.py b/continuedev/src/continuedev/libs/steps/main.py index 555c1180..f28cb23f 100644 --- a/continuedev/src/continuedev/libs/steps/main.py +++ b/continuedev/src/continuedev/libs/steps/main.py @@ -1,18 +1,39 @@ -import time from typing import Callable, Coroutine, List, Union +from ..util.traceback_parsers import parse_python_traceback from ..llm import LLM from ...models.main import Traceback, Range from ...models.filesystem_edit import EditDiff, FileEdit from ...models.filesystem import RangeInFile, RangeInFileWithContents -from ..observation import Observation, TextObservation +from ...core.observation import Observation, TextObservation, TracebackObservation from ..llm.prompt_utils import MarkdownStyleEncoderDecoder from textwrap import dedent -from ..core import History, Policy, Step, ContinueSDK, Observation +from ...core.main import History, Policy, Step, ContinueSDK, Observation import subprocess -from ..util.traceback_parsers import parse_python_traceback -from ..observation import TracebackObservation import json +from .core.core import EditCodeStep + + +class RunCodeStep(Step): + cmd: str + + async def describe(self, llm: LLM) -> 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 RunPolicyUntilDoneStep(Step): @@ -51,149 +72,6 @@ class RunCommandStep(Step): return TextObservation(text=stdout) -def ShellCommandsStep(Step): - cmds: List[str] - name: str = "Run Shell Commands" - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return "\n".join(self.cmds) - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - cwd = await sdk.ide.getWorkspaceDirectory() - - process = subprocess.Popen( - '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd) - - stdin_input = "\n".join(self.cmds) - out, err = process.communicate(stdin_input.encode()) - - # TODO: How to await?? - - # If it fails, return the error - if err is not None and err != "": - return TextObservation(text=err) - - return None - - -class WaitForUserInputStep(Step): - prompt: str - name: str = "Waiting for user input" - - _description: Union[str, None] = None - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return self.prompt - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt - resp = await sdk.wait_for_user_input() - return TextObservation(text=resp) - - -class WaitForUserConfirmationStep(Step): - prompt: str - name: str = "Waiting for user confirmation" - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return self.prompt - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self._description = self.prompt - resp = await sdk.wait_for_user_input() - return TextObservation(text=resp) - - -class RunCodeStep(Step): - cmd: str - - async def describe(self, llm: LLM) -> 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 EditCodeStep(Step): - # Might make an even more specific atomic step, which is "apply file edit" - range_in_files: List[RangeInFile] - prompt: str # String with {code} somewhere - name: str = "Edit code" - - _edit_diffs: Union[List[EditDiff], None] = None - _prompt: Union[str, None] = None - _completion: Union[str, None] = None - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - if self._edit_diffs is None: - return "Editing files: " + ", ".join(map(lambda rif: rif.filepath, self.range_in_files)) - elif len(self._edit_diffs) == 0: - return "No edits made" - else: - return llm.complete(dedent(f"""{self._prompt}{self._completion} - - Maximally concise summary of changes in bullet points (can use markdown): - """)) - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - rif_with_contents = [] - for range_in_file in self.range_in_files: - file_contents = await sdk.ide.readRangeInFile(range_in_file) - rif_with_contents.append( - RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) - enc_dec = MarkdownStyleEncoderDecoder(rif_with_contents) - code_string = enc_dec.encode() - prompt = self.prompt.format(code=code_string) - - completion = sdk.llm.complete(prompt) - - # Temporarily doing this to generate description. - self._prompt = prompt - self._completion = completion - - file_edits = enc_dec.decode(completion) - - self._edit_diffs = [] - for file_edit in file_edits: - diff = await sdk.apply_filesystem_edit(file_edit) - self._edit_diffs.append(diff) - - for filepath in set([file_edit.filepath for file_edit in file_edits]): - await sdk.ide.saveFile(filepath) - await sdk.ide.setFileOpen(filepath) - - return None - - -class EditFileStep(Step): - filepath: str - prompt: str - hide: bool = True - - async def describe(self, llm: LLM) -> Coroutine[str, None, None]: - return "Editing file: " + self.filepath - - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - file_contents = await sdk.ide.readFile(self.filepath) - await sdk.run_step(EditCodeStep( - range_in_files=[RangeInFile.from_entire_file( - self.filepath, file_contents)], - prompt=self.prompt - )) - - class FasterEditHighlightedCodeStep(Step): user_input: str hide = True diff --git a/continuedev/src/continuedev/libs/steps/migration.py b/continuedev/src/continuedev/libs/steps/migration.py index 04296836..f044a60f 100644 --- a/continuedev/src/continuedev/libs/steps/migration.py +++ b/continuedev/src/continuedev/libs/steps/migration.py @@ -3,7 +3,7 @@ from ...models.filesystem import RangeInFile from .main import EditCodeStep, RunCommandStep -from ..core import Step +from ...core.main import Step class MigrationStep(Step): diff --git a/continuedev/src/continuedev/libs/steps/nate.py b/continuedev/src/continuedev/libs/steps/nate.py index 80436fa4..a0e728e5 100644 --- a/continuedev/src/continuedev/libs/steps/nate.py +++ b/continuedev/src/continuedev/libs/steps/nate.py @@ -1,14 +1,13 @@ -import asyncio from textwrap import dedent import time from typing import Coroutine, Union -from ...models.main import Range from ...models.filesystem import RangeInFile from ...models.filesystem_edit import AddDirectory, AddFile -from ..observation import Observation, TextObservation -from ..core import Step, ContinueSDK -from .main import EditCodeStep, EditFileStep, RunCommandStep, WaitForUserConfirmationStep +from ...core.observation import Observation, TextObservation +from ...core.main import Step, ContinueSDK +from .main import RunCommandStep +from .core.core import WaitForUserConfirmationStep, EditCodeStep, EditFileStep import os diff --git a/continuedev/src/continuedev/libs/steps/pytest.py b/continuedev/src/continuedev/libs/steps/pytest.py index e53eb465..b4e6dfd2 100644 --- a/continuedev/src/continuedev/libs/steps/pytest.py +++ b/continuedev/src/continuedev/libs/steps/pytest.py @@ -1,6 +1,6 @@ from textwrap import dedent from ...models.filesystem_edit import AddDirectory, AddFile -from ..core import Step, ContinueSDK +from ...core.main import Step, ContinueSDK import os diff --git a/continuedev/src/continuedev/libs/steps/ty.py b/continuedev/src/continuedev/libs/steps/ty.py index 1eb6271d..5ff03f04 100644 --- a/continuedev/src/continuedev/libs/steps/ty.py +++ b/continuedev/src/continuedev/libs/steps/ty.py @@ -2,9 +2,11 @@ import subprocess from ...models.main import Position, Range from ...models.filesystem import RangeInFile from ...models.filesystem_edit import AddDirectory, AddFile, FileEdit -from ..observation import DictObservation -from ..core import History, Step, ContinueSDK, Policy -from .main import EditCodeStep, RunCommandStep, WaitForUserInputStep, WaitForUserConfirmationStep +from ...core.observation import DictObservation +from ...core.main import History, Step, Policy +from ...core.sdk import ContinueSDK +from .main import RunCommandStep +from ..steps.core.core import EditCodeStep, WaitForUserConfirmationStep, WaitForUserInputStep source_name = "weather_api" diff --git a/continuedev/src/continuedev/plugins/policy/libs/alternate.py b/continuedev/src/continuedev/plugins/policy/libs/alternate.py index 5bfbb821..3087c059 100644 --- a/continuedev/src/continuedev/plugins/policy/libs/alternate.py +++ b/continuedev/src/continuedev/plugins/policy/libs/alternate.py @@ -1,7 +1,5 @@ from plugins import policy -from ....libs.observation import Observation -from ....libs.steps import Step -from ....libs.core import History +from ....core.main import History, Step class AlternatingPolicy: diff --git a/continuedev/src/continuedev/plugins/step/hookspecs.py b/continuedev/src/continuedev/plugins/step/hookspecs.py index 4309bad3..a5714fc5 100644 --- a/continuedev/src/continuedev/plugins/step/hookspecs.py +++ b/continuedev/src/continuedev/plugins/step/hookspecs.py @@ -1,6 +1,8 @@ from typing import Coroutine import pluggy -from ...libs.core import ContinueSDK, Step, Observation +from ...core.main import Step +from ...core.observation import Observation +from ...core.sdk import ContinueSDK hookspec = pluggy.HookspecMarker("continue.step") diff --git a/continuedev/src/continuedev/server/notebook.py b/continuedev/src/continuedev/server/notebook.py index 5eb151d7..bfd7a09c 100644 --- a/continuedev/src/continuedev/server/notebook.py +++ b/continuedev/src/continuedev/server/notebook.py @@ -5,14 +5,14 @@ from pydantic import BaseModel from uvicorn.main import Server from ..models.filesystem_edit import FileEditWithFullContents -from ..libs.policy import DemoPolicy -from ..libs.core import FullState, History, Step -from ..libs.agent import Agent +from ..core.policy import DemoPolicy +from ..core.main import FullState, History, Step +from ..core.agent import Agent from ..libs.steps.nate import ImplementAbstractMethodStep -from ..libs.observation import Observation +from ..core.observation import Observation from ..libs.llm.openai import OpenAI from .ide_protocol import AbstractIdeProtocolServer -from ..libs.env import openai_api_key +from ..core.env import openai_api_key import asyncio import nest_asyncio nest_asyncio.apply() -- cgit v1.2.3-70-g09d2 From 04e0ebd6ef7c3ba15d8210f5e6c879db2894ed03 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sat, 27 May 2023 15:58:34 -0400 Subject: Added History classes for JSON Schema generation --- .../src/continuedev/models/generate_json_schema.py | 3 + extension/package.json | 2 +- extension/schema/History.d.ts | 41 + extension/schema/HistoryNode.d.ts | 31 + .../scripts/continuedev-0.1.0-py3-none-any.whl | Bin 61142 -> 51468 bytes schema/json/History.json | 73 ++ schema/json/HistoryNode.json | 51 + schema/openapi.json | 1027 ++++++++++++++++++++ 8 files changed, 1227 insertions(+), 1 deletion(-) create mode 100644 extension/schema/History.d.ts create mode 100644 extension/schema/HistoryNode.d.ts create mode 100644 schema/json/History.json create mode 100644 schema/json/HistoryNode.json create mode 100644 schema/openapi.json (limited to 'continuedev') diff --git a/continuedev/src/continuedev/models/generate_json_schema.py b/continuedev/src/continuedev/models/generate_json_schema.py index da78dfac..07337029 100644 --- a/continuedev/src/continuedev/models/generate_json_schema.py +++ b/continuedev/src/continuedev/models/generate_json_schema.py @@ -1,6 +1,7 @@ from .main import * from .filesystem import RangeInFile, FileEdit from .filesystem_edit import FileEditWithFullContents +from ..core.main import History, HistoryNode from pydantic import schema_json_of import os @@ -10,6 +11,8 @@ MODELS_TO_GENERATE = [ RangeInFile, FileEdit ] + [ FileEditWithFullContents +] + [ + History, HistoryNode ] RENAMES = { diff --git a/extension/package.json b/extension/package.json index d957e071..3f9e59ff 100644 --- a/extension/package.json +++ b/extension/package.json @@ -166,7 +166,7 @@ "package": "cp ./config/prod_config.json ./config/config.json && npm run compile && mkdir -p ./build && vsce package --out ./build && chmod 777 ./build/continue-0.0.1.vsix && cp ./config/dev_config.json ./config/config.json", "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.0-py3-none-any.whl ../extension/scripts/continuedev-0.1.0-py3-none-any.whl && cd ../extension && npm run typegen && npm run clientgen && cd react-app && npm run build && cd .. && npm run package", "install-extension": "code --install-extension ./build/continue-0.0.1.vsix", - "uninstall": "code --uninstall-extension Continue.continue", + "uninstall": "code --uninstall-extension .continue", "reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension" }, "devDependencies": { diff --git a/extension/schema/History.d.ts b/extension/schema/History.d.ts new file mode 100644 index 00000000..508deaf0 --- /dev/null +++ b/extension/schema/History.d.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export type History = History1; +export type Name = string; +export type Hide = boolean; +export type SystemMessage = string; +export type Depth = number; +export type Timeline = HistoryNode[]; +export type CurrentIndex = number; + +/** + * A history of steps taken and their results + */ +export interface History1 { + timeline: Timeline; + current_index: CurrentIndex; + [k: string]: unknown; +} +/** + * A point in history, a list of which make up History + */ +export interface HistoryNode { + step: Step; + observation?: Observation; + depth: Depth; + [k: string]: unknown; +} +export interface Step { + name?: Name; + hide?: Hide; + system_message?: SystemMessage; + [k: string]: unknown; +} +export interface Observation { + [k: string]: unknown; +} diff --git a/extension/schema/HistoryNode.d.ts b/extension/schema/HistoryNode.d.ts new file mode 100644 index 00000000..c1507270 --- /dev/null +++ b/extension/schema/HistoryNode.d.ts @@ -0,0 +1,31 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export type HistoryNode = HistoryNode1; +export type Name = string; +export type Hide = boolean; +export type SystemMessage = string; +export type Depth = number; + +/** + * A point in history, a list of which make up History + */ +export interface HistoryNode1 { + step: Step; + observation?: Observation; + depth: Depth; + [k: string]: unknown; +} +export interface Step { + name?: Name; + hide?: Hide; + system_message?: SystemMessage; + [k: string]: unknown; +} +export interface Observation { + [k: string]: unknown; +} diff --git a/extension/scripts/continuedev-0.1.0-py3-none-any.whl b/extension/scripts/continuedev-0.1.0-py3-none-any.whl index 15787c59..68457b3e 100644 Binary files a/extension/scripts/continuedev-0.1.0-py3-none-any.whl and b/extension/scripts/continuedev-0.1.0-py3-none-any.whl differ diff --git a/schema/json/History.json b/schema/json/History.json new file mode 100644 index 00000000..7691c7dd --- /dev/null +++ b/schema/json/History.json @@ -0,0 +1,73 @@ +{ + "title": "History", + "$ref": "#/definitions/continuedev__src__continuedev__core__main__History", + "definitions": { + "Step": { + "title": "Step", + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "hide": { + "title": "Hide", + "default": false, + "type": "boolean" + }, + "system_message": { + "title": "System Message", + "type": "string" + } + } + }, + "Observation": { + "title": "Observation", + "type": "object", + "properties": {} + }, + "HistoryNode": { + "title": "HistoryNode", + "description": "A point in history, a list of which make up History", + "type": "object", + "properties": { + "step": { + "$ref": "#/definitions/Step" + }, + "observation": { + "$ref": "#/definitions/Observation" + }, + "depth": { + "title": "Depth", + "type": "integer" + } + }, + "required": [ + "step", + "depth" + ] + }, + "continuedev__src__continuedev__core__main__History": { + "title": "History", + "description": "A history of steps taken and their results", + "type": "object", + "properties": { + "timeline": { + "title": "Timeline", + "type": "array", + "items": { + "$ref": "#/definitions/HistoryNode" + } + }, + "current_index": { + "title": "Current Index", + "type": "integer" + } + }, + "required": [ + "timeline", + "current_index" + ] + } + } +} \ No newline at end of file diff --git a/schema/json/HistoryNode.json b/schema/json/HistoryNode.json new file mode 100644 index 00000000..f58b8038 --- /dev/null +++ b/schema/json/HistoryNode.json @@ -0,0 +1,51 @@ +{ + "title": "HistoryNode", + "$ref": "#/definitions/continuedev__src__continuedev__core__main__HistoryNode", + "definitions": { + "Step": { + "title": "Step", + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "hide": { + "title": "Hide", + "default": false, + "type": "boolean" + }, + "system_message": { + "title": "System Message", + "type": "string" + } + } + }, + "Observation": { + "title": "Observation", + "type": "object", + "properties": {} + }, + "continuedev__src__continuedev__core__main__HistoryNode": { + "title": "HistoryNode", + "description": "A point in history, a list of which make up History", + "type": "object", + "properties": { + "step": { + "$ref": "#/definitions/Step" + }, + "observation": { + "$ref": "#/definitions/Observation" + }, + "depth": { + "title": "Depth", + "type": "integer" + } + }, + "required": [ + "step", + "depth" + ] + } + } +} \ No newline at end of file diff --git a/schema/openapi.json b/schema/openapi.json new file mode 100644 index 00000000..8880fd20 --- /dev/null +++ b/schema/openapi.json @@ -0,0 +1,1027 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Continue API", + "description": "Continue API", + "version": "1.0" + }, + "paths": { + "/debug/run": { + "post": { + "tags": ["debug"], + "summary": "Run", + "description": "Returns boolean indicating whether error was found, edited, and solved, or not all of these.", + "operationId": "run_debug_run_post", + "parameters": [ + { + "required": true, + "schema": { + "title": "Filepath", + "type": "string" + }, + "name": "filepath", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Make Edit", + "type": "boolean", + "default": false + }, + "name": "make_edit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/inline": { + "post": { + "tags": ["debug"], + "summary": "Inline", + "operationId": "inline_debug_inline_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InlineBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/suggestion": { + "get": { + "tags": ["debug"], + "summary": "Suggestion", + "operationId": "suggestion_debug_suggestion_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Traceback", + "type": "string" + }, + "name": "traceback", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/list": { + "post": { + "tags": ["debug"], + "summary": "Listten", + "operationId": "listten_debug_list_post", + "parameters": [ + { + "required": false, + "schema": { + "title": "X-Vsc-Machine-Id", + "type": "string", + "default": "anonymous" + }, + "name": "x-vsc-machine-id", + "in": "header" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SerializedDebugContext" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/explain": { + "post": { + "tags": ["debug"], + "summary": "Explain", + "operationId": "explain_debug_explain_post", + "parameters": [ + { + "required": false, + "schema": { + "title": "X-Vsc-Machine-Id", + "type": "string", + "default": "anonymous" + }, + "name": "x-vsc-machine-id", + "in": "header" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SerializedDebugContext" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExplainResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/edit": { + "post": { + "tags": ["debug"], + "summary": "Edit Endpoint", + "operationId": "edit_endpoint_debug_edit_post", + "parameters": [ + { + "required": false, + "schema": { + "title": "X-Vsc-Machine-Id", + "type": "string", + "default": "anonymous" + }, + "name": "x-vsc-machine-id", + "in": "header" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SerializedDebugContext" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EditResp" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/find": { + "post": { + "tags": ["debug"], + "summary": "Find Sus Code Endpoint", + "operationId": "find_sus_code_endpoint_debug_find_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FindBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FindResp" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/parse_traceback": { + "get": { + "tags": ["debug"], + "summary": "Parse Traceback Endpoint", + "operationId": "parse_traceback_endpoint_debug_parse_traceback_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Traceback", + "type": "string" + }, + "name": "traceback", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Traceback" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/debug/find_docs": { + "get": { + "tags": ["debug"], + "summary": "Find Docs Endpoint", + "operationId": "find_docs_endpoint_debug_find_docs_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Traceback", + "type": "string" + }, + "name": "traceback", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OptionalCompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/docstring/forline": { + "get": { + "tags": ["docstring"], + "summary": "Forline", + "description": "Write a docstring for a function at a line number", + "operationId": "forline_docstring_forline_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Filecontents", + "type": "string" + }, + "name": "filecontents", + "in": "query" + }, + { + "required": true, + "schema": { + "title": "Lineno", + "type": "integer" + }, + "name": "lineno", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Format", + "type": "string", + "default": "google" + }, + "name": "format", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "X-Vsc-Machine-Id", + "type": "string", + "default": "anonymous" + }, + "name": "x-vsc-machine-id", + "in": "header" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/unittest/forline": { + "post": { + "tags": ["unittest"], + "summary": "Forline", + "description": "Write unit test for the function encapsulating the given line number.", + "operationId": "forline_unittest_forline_post", + "parameters": [ + { + "required": false, + "schema": { + "title": "X-Vsc-Machine-Id", + "type": "string", + "default": "anonymous" + }, + "name": "x-vsc-machine-id", + "in": "header" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilePosition" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/unittest/failingtest": { + "post": { + "tags": ["unittest"], + "summary": "Failingtest", + "description": "Write a failing test for the function encapsulating the given line number.", + "operationId": "failingtest_unittest_failingtest_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailingTestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompletionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/chat/test": { + "get": { + "tags": ["chat"], + "summary": "Test", + "operationId": "test_chat_test_get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Prompt", + "type": "string" + }, + "name": "prompt", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/chat/complete": { + "post": { + "tags": ["chat"], + "summary": "Complete", + "operationId": "complete_chat_complete_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatHistory" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/test": { + "get": { + "summary": "Test", + "operationId": "test_test_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ChatHistory": { + "title": "ChatHistory", + "required": ["messages"], + "type": "object", + "properties": { + "messages": { + "title": "Messages", + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatMessage" + } + } + } + }, + "ChatMessage": { + "title": "ChatMessage", + "required": ["role", "content"], + "type": "object", + "properties": { + "role": { + "title": "Role", + "type": "string" + }, + "content": { + "title": "Content", + "type": "string" + } + } + }, + "CompletionResponse": { + "title": "CompletionResponse", + "required": ["completion"], + "type": "object", + "properties": { + "completion": { + "title": "Completion", + "type": "string" + } + } + }, + "EditResp": { + "title": "EditResp", + "required": ["completion"], + "type": "object", + "properties": { + "completion": { + "title": "Completion", + "type": "array", + "items": { + "$ref": "#/components/schemas/FileEdit" + } + } + } + }, + "ExplainResponse": { + "title": "ExplainResponse", + "required": ["completion"], + "type": "object", + "properties": { + "completion": { + "title": "Completion", + "type": "string" + } + } + }, + "FailingTestBody": { + "title": "FailingTestBody", + "required": ["description", "fp"], + "type": "object", + "properties": { + "description": { + "title": "Description", + "type": "string" + }, + "fp": { + "$ref": "#/components/schemas/FilePosition" + } + }, + "description": "A failing test body." + }, + "FileEdit": { + "title": "FileEdit", + "required": ["filepath", "range", "replacement"], + "type": "object", + "properties": { + "filepath": { + "title": "Filepath", + "type": "string" + }, + "range": { + "$ref": "#/components/schemas/Range" + }, + "replacement": { + "title": "Replacement", + "type": "string" + } + }, + "additionalProperties": false + }, + "FilePosition": { + "title": "FilePosition", + "required": ["filecontents", "lineno"], + "type": "object", + "properties": { + "filecontents": { + "title": "Filecontents", + "type": "string" + }, + "lineno": { + "title": "Lineno", + "type": "integer" + } + }, + "description": "A position in a file." + }, + "FindBody": { + "title": "FindBody", + "required": ["traceback", "filesystem"], + "type": "object", + "properties": { + "traceback": { + "title": "Traceback", + "type": "string" + }, + "filesystem": { + "title": "Filesystem", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "title": "Description", + "type": "string" + } + } + }, + "FindResp": { + "title": "FindResp", + "required": ["response"], + "type": "object", + "properties": { + "response": { + "title": "Response", + "type": "array", + "items": { + "$ref": "#/components/schemas/RangeInFile" + } + } + } + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + }, + "InlineBody": { + "title": "InlineBody", + "required": ["filecontents", "startline", "endline"], + "type": "object", + "properties": { + "filecontents": { + "title": "Filecontents", + "type": "string" + }, + "startline": { + "title": "Startline", + "type": "integer" + }, + "endline": { + "title": "Endline", + "type": "integer" + }, + "traceback": { + "title": "Traceback", + "type": "string", + "default": "" + } + } + }, + "OptionalCompletionResponse": { + "title": "OptionalCompletionResponse", + "type": "object", + "properties": { + "completion": { + "title": "Completion", + "type": "string" + } + } + }, + "Position": { + "title": "Position", + "required": ["line", "character"], + "type": "object", + "properties": { + "line": { + "title": "Line", + "type": "integer" + }, + "character": { + "title": "Character", + "type": "integer" + } + }, + "additionalProperties": false + }, + "ProgrammingLangauge": { + "title": "ProgrammingLangauge", + "enum": ["python", "javascript", "typescript"], + "type": "string", + "description": "An enumeration." + }, + "Range": { + "title": "Range", + "required": ["start", "end"], + "type": "object", + "properties": { + "start": { + "$ref": "#/components/schemas/Position" + }, + "end": { + "$ref": "#/components/schemas/Position" + } + }, + "additionalProperties": false, + "description": "A range in a file. 0-indexed." + }, + "RangeInFile": { + "title": "RangeInFile", + "required": ["filepath", "range"], + "type": "object", + "properties": { + "filepath": { + "title": "Filepath", + "type": "string" + }, + "range": { + "$ref": "#/components/schemas/Range" + } + }, + "additionalProperties": false + }, + "SerializedDebugContext": { + "title": "SerializedDebugContext", + "required": ["ranges_in_files", "filesystem"], + "type": "object", + "properties": { + "traceback": { + "title": "Traceback", + "type": "string" + }, + "ranges_in_files": { + "title": "Ranges In Files", + "type": "array", + "items": { + "$ref": "#/components/schemas/RangeInFile" + } + }, + "filesystem": { + "title": "Filesystem", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "title": "Description", + "type": "string" + } + } + }, + "Traceback": { + "title": "Traceback", + "required": ["frames", "message", "error_type", "language"], + "type": "object", + "properties": { + "frames": { + "title": "Frames", + "type": "array", + "items": { + "$ref": "#/components/schemas/TracebackFrame" + } + }, + "message": { + "title": "Message", + "type": "string" + }, + "error_type": { + "title": "Error Type", + "type": "string" + }, + "language": { + "$ref": "#/components/schemas/ProgrammingLangauge" + }, + "full_traceback": { + "title": "Full Traceback", + "type": "string" + } + }, + "additionalProperties": false + }, + "TracebackFrame": { + "title": "TracebackFrame", + "required": ["filepath", "lineno", "function"], + "type": "object", + "properties": { + "filepath": { + "title": "Filepath", + "type": "string" + }, + "lineno": { + "title": "Lineno", + "type": "integer" + }, + "function": { + "title": "Function", + "type": "string" + }, + "code": { + "title": "Code", + "type": "string" + } + }, + "additionalProperties": false + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + } + } + } +} -- cgit v1.2.3-70-g09d2