import html import json import os from textwrap import dedent from typing import Any, Coroutine, List from directory_tree import display_tree from dotenv import load_dotenv from pydantic import Field from ...core.main import ChatMessage, Models, Step, step_to_json_schema from ...core.sdk import ContinueSDK from ...libs.util.devdata import dev_data_logger from ...libs.util.strings import remove_quotes_and_escapes from ...libs.util.telemetry import posthog_logger load_dotenv() def add_ellipsis(text: str, max_length: int = 200) -> str: if len(text) > max_length: return text[: max_length - 3] + "..." return text class SimpleChatStep(Step): name: str = "Generating Response..." manage_own_chat_context: bool = True description: str = "" messages: List[ChatMessage] = None async def run(self, sdk: ContinueSDK): # Check if proxy server API key messages = self.messages or await sdk.get_chat_context() generator = sdk.models.chat.stream_chat( messages, temperature=sdk.config.temperature ) posthog_logger.capture_event( "model_use", { "model": sdk.models.default.model, "provider": sdk.models.default.__class__.__name__, }, ) dev_data_logger.capture( "model_use", { "model": sdk.models.default.model, "provider": sdk.models.default.__class__.__name__, }, ) async for chunk in generator: if sdk.current_step_was_deleted(): # So that the message doesn't disappear self.hide = False await sdk.update_ui() break if "content" in chunk: self.description += chunk["content"] # HTML unencode end_size = len(chunk["content"]) - 6 if "&" in self.description[-end_size:]: self.description = self.description[:-end_size] + html.unescape( self.description[-end_size:] ) await sdk.update_ui() if sdk.config.disable_summaries: self.name = "" else: self.name = "Generating title..." await sdk.update_ui() self.name = add_ellipsis( remove_quotes_and_escapes( await sdk.models.summarize.complete( f'"{self.description}"\n\nPlease write a short title summarizing the message quoted above. Use no more than 10 words:', max_tokens=20, log=False, ) ), 200, ) await sdk.update_ui() self.chat_context.append( ChatMessage(role="assistant", content=self.description, summary=self.name) ) # TODO: Never actually closing. await generator.aclose() class AddFileStep(Step): name: str = "Add File" description = "Add a file to the workspace. Should always view the directory tree before this." filename: str file_contents: str async def describe( self, models: Models ) -> Coroutine[Any, Any, Coroutine[str, None, None]]: return f"Added a file named `{self.filename}` to the workspace." async def run(self, sdk: ContinueSDK): await sdk.add_file(self.filename, self.file_contents) await sdk.ide.setFileOpen( os.path.join(sdk.ide.workspace_directory, self.filename) ) class DeleteFileStep(Step): name: str = "Delete File" description = "Delete a file from the workspace." filename: str async def describe( self, models: Models ) -> Coroutine[Any, Any, Coroutine[str, None, None]]: return f"Deleted a file named `{self.filename}` from the workspace." async def run(self, sdk: ContinueSDK): await sdk.delete_file(self.filename) class AddDirectoryStep(Step): name: str = "Add Directory" description = "Add a directory to the workspace." directory_name: str async def describe( self, models: Models ) -> Coroutine[Any, Any, Coroutine[str, None, None]]: return f"Added a directory named `{self.directory_name}` to the workspace." async def run(self, sdk: ContinueSDK): try: await sdk.add_directory(self.directory_name) except FileExistsError: self.description = f"Directory {self.directory_name} already exists." class RunTerminalCommandStep(Step): name: str = "Run Terminal Command" description: str = "Run a terminal command." command: str async def run(self, sdk: ContinueSDK): self.description = f"Copy this command and run in your terminal:\n\n```bash\n{self.command}\n```" class ViewDirectoryTreeStep(Step): name: str = "View Directory Tree" description: str = "View the directory tree to learn which folder and files exist. You should always do this before adding new files." async def describe( self, models: Models ) -> Coroutine[Any, Any, Coroutine[str, None, None]]: return "Viewed the directory tree." async def run(self, sdk: ContinueSDK): self.description = ( f"```\n{display_tree(sdk.ide.workspace_directory, True, max_depth=2)}\n```" ) class EditFileStep(Step): name: str = "Edit File" description: str = "Edit a file in the workspace that is not currently open." filename: str = Field(..., description="The name of the file to edit.") instructions: str = Field(..., description="The instructions to edit the file.") hide: bool = True async def run(self, sdk: ContinueSDK): await sdk.edit_file(self.filename, self.instructions)