diff options
author | Nate Sesti <33237525+sestinj@users.noreply.github.com> | 2023-09-23 13:06:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-23 13:06:00 -0700 |
commit | e976d60974a7837967d03807605cbf2e7b4f3f9a (patch) | |
tree | 5ecb19062abb162832530dd953e9d2801026c23c /continuedev/src/continuedev/server | |
parent | 470711d25b44d1a545c57bc17d40d5e1fd402216 (diff) | |
download | sncontinue-e976d60974a7837967d03807605cbf2e7b4f3f9a.tar.gz sncontinue-e976d60974a7837967d03807605cbf2e7b4f3f9a.tar.bz2 sncontinue-e976d60974a7837967d03807605cbf2e7b4f3f9a.zip |
UI Redesign and fixing many details (#496)
* feat: :lipstick: start of major design upgrade
* feat: :lipstick: model selection page
* feat: :lipstick: use shortcut to add highlighted code as ctx
* feat: :lipstick: better display of errors
* feat: :lipstick: ui for learning keyboard shortcuts, more details
* refactor: :construction: testing slash commands ui
* Truncate continue.log
* refactor: :construction: refactoring client_session, ui, more
* feat: :bug: layout fixes
* refactor: :lipstick: ui to enter OpenAI Key
* refactor: :truck: rename MaybeProxyOpenAI -> OpenAIFreeTrial
* starting help center
* removing old shortcut docs
* fix: :bug: fix model setting logic to avoid overwrites
* feat: :lipstick: tutorial and model descriptions
* refactor: :truck: rename unused -> saved
* refactor: :truck: rename model roles
* feat: :lipstick: edit indicator
* refactor: :lipstick: move +, folder icons
* feat: :lipstick: tab to clear all context
* fix: :bug: context providers ui fixes
* fix: :bug: fix lag when stopping step
* fix: :bug: don't override system message for models
* fix: :bug: fix continue button cursor
* feat: :lipstick: title bar
* fix: :bug: updates to code highlighting logic and more
* fix: :bug: fix renaming of summarize model role
* feat: :lipstick: help page and better session title
* feat: :lipstick: more help page / ui improvements
* feat: :lipstick: set session title
* fix: :bug: small fixes for changing sessions
* fix: :bug: perfecting the highlighting code and ctx interactions
* style: :lipstick: sticky headers for scroll, ollama warming
* fix: :bug: fix toggle bug
---------
Co-authored-by: Ty Dunn <ty@tydunn.com>
Diffstat (limited to 'continuedev/src/continuedev/server')
-rw-r--r-- | continuedev/src/continuedev/server/gui.py | 88 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide.py | 28 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide_protocol.py | 8 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/meilisearch_server.py | 2 |
4 files changed, 88 insertions, 38 deletions
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 770065ac..9d2ea47a 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -82,7 +82,9 @@ class GUIProtocolServer: return resp_model.parse_obj(resp) def on_error(self, e: Exception): - return self.session.autopilot.continue_sdk.run_step(DisplayErrorStep(e=e)) + return self.session.autopilot.continue_sdk.run_step( + DisplayErrorStep.from_exception(e) + ) def handle_json(self, message_type: str, data: Any): if message_type == "main_input": @@ -97,6 +99,8 @@ class GUIProtocolServer: self.on_retry_at_index(data["index"]) elif message_type == "clear_history": self.on_clear_history() + elif message_type == "set_current_session_title": + self.set_current_session_title(data["title"]) elif message_type == "delete_at_index": self.on_delete_at_index(data["index"]) elif message_type == "delete_context_with_ids": @@ -107,6 +111,8 @@ class GUIProtocolServer: self.on_set_editing_at_ids(data["ids"]) elif message_type == "show_logs_at_index": self.on_show_logs_at_index(data["index"]) + elif message_type == "show_context_virtual_file": + self.show_context_virtual_file() elif message_type == "select_context_item": self.select_context_item(data["id"], data["query"]) elif message_type == "load_session": @@ -180,11 +186,9 @@ class GUIProtocolServer: create_async_task(self.session.autopilot.set_editing_at_ids(ids), self.on_error) def on_show_logs_at_index(self, index: int): - name = "continue_logs.txt" + name = "Continue Context" logs = "\n\n############################################\n\n".join( - [ - "This is a log of the prompt/completion pairs sent/received from the LLM during this step" - ] + ["This is the prompt sent to the LLM during this step"] + self.session.autopilot.continue_sdk.history.timeline[index].logs ) create_async_task( @@ -192,6 +196,22 @@ class GUIProtocolServer: ) posthog_logger.capture_event("show_logs_at_index", {}) + def show_context_virtual_file(self): + async def async_stuff(): + msgs = await self.session.autopilot.continue_sdk.get_chat_context() + ctx = "\n\n-----------------------------------\n\n".join( + ["This is the exact context that will be passed to the LLM"] + + list(map(lambda x: x.content, msgs)) + ) + await self.session.autopilot.ide.showVirtualFile( + "Continue - Selected Context", ctx + ) + + create_async_task( + async_stuff(), + self.on_error, + ) + def select_context_item(self, id: str, query: str): """Called when user selects an item from the dropdown""" create_async_task( @@ -211,6 +231,9 @@ class GUIProtocolServer: posthog_logger.capture_event("load_session", {"session_id": session_id}) + def set_current_session_title(self, title: str): + self.session.autopilot.set_current_session_title(title) + def set_system_message(self, message: str): self.session.autopilot.continue_sdk.config.system_message = message self.session.autopilot.continue_sdk.models.set_system_message(message) @@ -239,14 +262,14 @@ class GUIProtocolServer: # Set models in SDK temp = models.default - models.default = models.unused[index] - models.unused[index] = temp + models.default = models.saved[index] + models.saved[index] = temp await self.session.autopilot.continue_sdk.start_model(models.default) # Set models in config.py JOINER = ",\n\t\t" models_args = { - "unused": f"[{JOINER.join([display_llm_class(llm) for llm in models.unused])}]", + "saved": f"[{JOINER.join([display_llm_class(llm) for llm in models.saved])}]", ("default" if role == "*" else role): display_llm_class(models.default), } @@ -265,48 +288,59 @@ class GUIProtocolServer: def add_model_for_role(self, role: str, model_class: str, model: Any): models = self.session.autopilot.continue_sdk.config.models - unused_models = models.unused if role == "*": async def async_stuff(): - for role in ALL_MODEL_ROLES: - models.__setattr__(role, None) - - # Set and start the default model if didn't already exist from unused - models.default = MODEL_CLASSES[model_class](**model) - await self.session.autopilot.continue_sdk.run_step( - SetupModelStep(model_class=model_class) + # Remove all previous models in roles and place in saved + saved_models = models.saved + existing_saved_models = set( + [display_llm_class(llm) for llm in saved_models] ) - - await self.session.autopilot.continue_sdk.start_model(models.default) - - models_args = {} - for role in ALL_MODEL_ROLES: val = models.__getattribute__(role) - if val is None: - continue # no pun intended + if ( + val is not None + and display_llm_class(val) not in existing_saved_models + ): + saved_models.append(val) + existing_saved_models.add(display_llm_class(val)) + models.__setattr__(role, None) - models_args[role] = display_llm_class(val, True) + # Set and start the new default model + new_model = MODEL_CLASSES[model_class](**model) + models.default = new_model + await self.session.autopilot.continue_sdk.start_model(models.default) + # Construct and set the new models object JOINER = ",\n\t\t" - models_args[ - "unused" - ] = f"[{JOINER.join([display_llm_class(llm) for llm in unused_models])}]" + saved_model_strings = set( + [display_llm_class(llm) for llm in saved_models] + ) + models_args = { + "default": display_llm_class(models.default, True), + "saved": f"[{JOINER.join(saved_model_strings)}]", + } await self.session.autopilot.set_config_attr( ["models"], create_obj_node("Models", models_args), ) + # Add the requisite import to config.py add_config_import( f"from continuedev.src.continuedev.libs.llm.{MODEL_MODULE_NAMES[model_class]} import {model_class}" ) + # Set all roles (in-memory) to the new default model for role in ALL_MODEL_ROLES: if role != "default": models.__setattr__(role, models.default) + # Display setup help + await self.session.autopilot.continue_sdk.run_step( + SetupModelStep(model_class=model_class) + ) + create_async_task(async_stuff(), self.on_error) else: # TODO diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 7396b1db..d4f0690b 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -4,7 +4,7 @@ import json import os import traceback import uuid -from typing import Any, Callable, Coroutine, List, Type, TypeVar, Union +from typing import Any, Callable, Coroutine, List, Optional, Type, TypeVar, Union import nest_asyncio from fastapi import APIRouter, WebSocket @@ -232,7 +232,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): self.onFileEdits(fileEdits) elif message_type == "highlightedCodePush": self.onHighlightedCodeUpdate( - [RangeInFileWithContents(**rif) for rif in data["highlightedCode"]] + [RangeInFileWithContents(**rif) for rif in data["highlightedCode"]], + edit=data.get("edit", None), ) elif message_type == "commandOutput": output = data["output"] @@ -243,7 +244,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): elif message_type == "acceptRejectSuggestion": self.onAcceptRejectSuggestion(data["accepted"]) elif message_type == "acceptRejectDiff": - self.onAcceptRejectDiff(data["accepted"]) + self.onAcceptRejectDiff(data["accepted"], data["stepIndex"]) elif message_type == "mainUserInput": self.onMainUserInput(data["input"]) elif message_type == "deleteAtIndex": @@ -349,10 +350,17 @@ class IdeProtocolServer(AbstractIdeProtocolServer): posthog_logger.capture_event("accept_reject_suggestion", {"accepted": accepted}) dev_data_logger.capture("accept_reject_suggestion", {"accepted": accepted}) - def onAcceptRejectDiff(self, accepted: bool): + def onAcceptRejectDiff(self, accepted: bool, step_index: int): posthog_logger.capture_event("accept_reject_diff", {"accepted": accepted}) dev_data_logger.capture("accept_reject_diff", {"accepted": accepted}) + if not accepted: + if autopilot := self.__get_autopilot(): + create_async_task( + autopilot.reject_diff(step_index), + self.on_error, + ) + def onFileSystemUpdate(self, update: FileSystemEdit): # Access to Autopilot (so SessionManager) pass @@ -387,10 +395,14 @@ class IdeProtocolServer(AbstractIdeProtocolServer): if autopilot := self.__get_autopilot(): create_async_task(autopilot.handle_debug_terminal(content), self.on_error) - def onHighlightedCodeUpdate(self, range_in_files: List[RangeInFileWithContents]): + def onHighlightedCodeUpdate( + self, + range_in_files: List[RangeInFileWithContents], + edit: Optional[bool] = False, + ): if autopilot := self.__get_autopilot(): create_async_task( - autopilot.handle_highlighted_code(range_in_files), self.on_error + autopilot.handle_highlighted_code(range_in_files, edit), self.on_error ) ## Subscriptions ## @@ -456,7 +468,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): resp = await self._send_and_receive_json( {"commands": commands}, TerminalContentsResponse, "getTerminalContents" ) - return resp.contents + return resp.contents.strip() async def getHighlightedCode(self) -> List[RangeInFile]: resp = await self._send_and_receive_json( @@ -640,7 +652,7 @@ async def websocket_endpoint(websocket: WebSocket, session_id: str = None): if session_id is not None and session_id in session_manager.sessions: await session_manager.sessions[session_id].autopilot.continue_sdk.run_step( - DisplayErrorStep(e=e) + DisplayErrorStep.from_exception(e) ) elif ideProtocolServer is not None: await ideProtocolServer.showMessage(f"Error in Continue server: {err_msg}") diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 34030047..015da767 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Callable, List, Union +from typing import Any, Callable, List, Optional, Union from fastapi import WebSocket @@ -104,7 +104,11 @@ class AbstractIdeProtocolServer(ABC): """Run a command""" @abstractmethod - def onHighlightedCodeUpdate(self, range_in_files: List[RangeInFileWithContents]): + def onHighlightedCodeUpdate( + self, + range_in_files: List[RangeInFileWithContents], + edit: Optional[bool] = False, + ): """Called when highlighted code is updated""" @abstractmethod diff --git a/continuedev/src/continuedev/server/meilisearch_server.py b/continuedev/src/continuedev/server/meilisearch_server.py index 40d46b18..5e6cdd53 100644 --- a/continuedev/src/continuedev/server/meilisearch_server.py +++ b/continuedev/src/continuedev/server/meilisearch_server.py @@ -78,7 +78,7 @@ async def ensure_meilisearch_installed() -> bool: pass existing_paths.remove(meilisearchPath) - await download_meilisearch() + await download_meilisearch() # Clear the existing directories for p in existing_paths: |