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/core | |
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/core')
-rw-r--r-- | continuedev/src/continuedev/core/autopilot.py | 124 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/config.py | 6 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/main.py | 6 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/models.py | 24 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/sdk.py | 2 |
5 files changed, 110 insertions, 52 deletions
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index a27b0cb7..5804ce6b 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -10,6 +10,7 @@ from aiohttp import ClientPayloadError from openai import error as openai_errors from pydantic import root_validator +from ..libs.llm.prompts.chat import template_alpaca_messages from ..libs.util.create_async_task import create_async_task from ..libs.util.devdata import dev_data_logger from ..libs.util.edit_config import edit_config_property @@ -201,7 +202,9 @@ class Autopilot(ContinueBaseModel): ) or [] ) - return custom_commands + slash_commands + cmds = custom_commands + slash_commands + cmds.sort(key=lambda x: x["name"] == "edit", reverse=True) + return cmds async def clear_history(self): # Reset history @@ -273,14 +276,16 @@ class Autopilot(ContinueBaseModel): await self._run_singular_step(step) async def handle_highlighted_code( - self, range_in_files: List[RangeInFileWithContents] + self, + range_in_files: List[RangeInFileWithContents], + edit: Optional[bool] = False, ): if "code" not in self.context_manager.context_providers: return # Add to context manager await self.context_manager.context_providers["code"].handle_highlighted_code( - range_in_files + range_in_files, edit ) await self.update_subscribers() @@ -292,7 +297,9 @@ class Autopilot(ContinueBaseModel): self._retry_queue.post(str(index), None) async def delete_at_index(self, index: int): - self.history.timeline[index].step.hide = True + if not self.history.timeline[index].active: + self.history.timeline[index].step.hide = True + self.history.timeline[index].deleted = True self.history.timeline[index].active = False @@ -476,9 +483,43 @@ class Autopilot(ContinueBaseModel): create_async_task( update_description(), - on_error=lambda e: self.continue_sdk.run_step(DisplayErrorStep(e=e)), + on_error=lambda e: self.continue_sdk.run_step( + DisplayErrorStep.from_exception(e) + ), ) + # Create the session title if not done yet + if self.session_info is None or self.session_info.title is None: + visible_nodes = list( + filter(lambda node: not node.step.hide, self.history.timeline) + ) + + user_input = None + should_create_title = False + for visible_node in visible_nodes: + if isinstance(visible_node.step, UserInputStep): + if user_input is None: + user_input = visible_node.step.user_input + else: + # More than one user input, so don't create title + should_create_title = False + break + elif user_input is None: + continue + else: + # Already have user input, now have the next step + should_create_title = True + break + + # Only create the title if the step after the first input is done + if should_create_title: + create_async_task( + self.create_title(backup=user_input), + on_error=lambda e: self.continue_sdk.run_step( + DisplayErrorStep.from_exception(e) + ), + ) + return observation async def run_from_step(self, step: "Step"): @@ -523,41 +564,43 @@ class Autopilot(ContinueBaseModel): self._should_halt = False return None - async def accept_user_input(self, user_input: str): - self._main_user_input_queue.append(user_input) - await self.update_subscribers() + def set_current_session_title(self, title: str): + self.session_info = SessionInfo( + title=title, + session_id=self.ide.session_id, + date_created=str(time.time()), + workspace_directory=self.ide.workspace_directory, + ) - # Use the first input to create title for session info, and make the session saveable - if self.session_info is None: + async def create_title(self, backup: str = None): + # Use the first input and first response to create title for session info, and make the session saveable + if self.session_info is not None and self.session_info.title is not None: + return - async def create_title(): - if ( - self.session_info is not None - and self.session_info.title is not None - ): - return + if self.continue_sdk.config.disable_summaries: + if backup is not None: + title = backup + else: + title = "New Session" + else: + chat_history = list( + map(lambda x: x.dict(), await self.continue_sdk.get_chat_context()) + ) + chat_history_str = template_alpaca_messages(chat_history) + title = await self.continue_sdk.models.summarize.complete( + f"{chat_history_str}\n\nGive a short title to describe the above chat session. Do not put quotes around the title. Do not use more than 6 words. The title is: ", + max_tokens=20, + log=False, + ) + title = remove_quotes_and_escapes(title) - if self.continue_sdk.config.disable_summaries: - title = user_input - else: - title = await self.continue_sdk.models.medium.complete( - f'Give a short title to describe the current chat session. Do not put quotes around the title. The first message was: "{user_input}". Do not use more than 10 words. The title is: ', - max_tokens=20, - ) - title = remove_quotes_and_escapes(title) - - self.session_info = SessionInfo( - title=title, - session_id=self.ide.session_id, - date_created=str(time.time()), - workspace_directory=self.ide.workspace_directory, - ) - dev_data_logger.capture("new_session", self.session_info.dict()) + self.set_current_session_title(title) + await self.update_subscribers() + dev_data_logger.capture("new_session", self.session_info.dict()) - create_async_task( - create_title(), - on_error=lambda e: self.continue_sdk.run_step(DisplayErrorStep(e=e)), - ) + async def accept_user_input(self, user_input: str): + self._main_user_input_queue.append(user_input) + await self.update_subscribers() if len(self._main_user_input_queue) > 1: return @@ -579,6 +622,15 @@ class Autopilot(ContinueBaseModel): await self.reverse_to_index(index) await self.run_from_step(UserInputStep(user_input=user_input)) + async def reject_diff(self, step_index: int): + # Hide the edit step and the UserInputStep before it + self.history.timeline[step_index].step.hide = True + for i in range(step_index - 1, -1, -1): + if isinstance(self.history.timeline[i].step, UserInputStep): + self.history.timeline[i].step.hide = True + break + await self.update_subscribers() + async def select_context_item(self, id: str, query: str): await self.context_manager.select_context_item(id, query) await self.update_subscribers() diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index d431c704..2bbb42cc 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from pydantic import BaseModel, Field, validator -from ..libs.llm.maybe_proxy_openai import MaybeProxyOpenAI +from ..libs.llm.openai_free_trial import OpenAIFreeTrial from .context import ContextProvider from .main import Policy, Step from .models import Models @@ -48,8 +48,8 @@ class ContinueConfig(BaseModel): ) models: Models = Field( Models( - default=MaybeProxyOpenAI(model="gpt-4"), - medium=MaybeProxyOpenAI(model="gpt-3.5-turbo"), + default=OpenAIFreeTrial(model="gpt-4"), + summarize=OpenAIFreeTrial(model="gpt-3.5-turbo"), ), description="Configuration for the models used by Continue. Read more about how to configure models in the documentation.", ) diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 63a3e6a9..cf41aab9 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -337,6 +337,12 @@ class Step(ContinueBaseModel): hide: bool = False description: Union[str, None] = None + class_name: str = "Step" + + @validator("class_name", pre=True, always=True) + def class_name_is_class_name(cls, class_name): + return cls.__name__ + system_message: Union[str, None] = None chat_context: List[ChatMessage] = [] manage_own_chat_context: bool = False diff --git a/continuedev/src/continuedev/core/models.py b/continuedev/src/continuedev/core/models.py index f24c81ca..2396a0db 100644 --- a/continuedev/src/continuedev/core/models.py +++ b/continuedev/src/continuedev/core/models.py @@ -5,13 +5,14 @@ from pydantic import BaseModel from ..libs.llm import LLM from ..libs.llm.anthropic import AnthropicLLM from ..libs.llm.ggml import GGML +from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI +from ..libs.llm.hf_tgi import HuggingFaceTGI from ..libs.llm.llamacpp import LlamaCpp -from ..libs.llm.maybe_proxy_openai import MaybeProxyOpenAI from ..libs.llm.ollama import Ollama from ..libs.llm.openai import OpenAI +from ..libs.llm.openai_free_trial import OpenAIFreeTrial from ..libs.llm.replicate import ReplicateLLM from ..libs.llm.together import TogetherLLM -from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI class ContinueSDK(BaseModel): @@ -20,9 +21,7 @@ class ContinueSDK(BaseModel): ALL_MODEL_ROLES = [ "default", - "small", - "medium", - "large", + "summarize", "edit", "chat", ] @@ -31,7 +30,7 @@ MODEL_CLASSES = { cls.__name__: cls for cls in [ OpenAI, - MaybeProxyOpenAI, + OpenAIFreeTrial, GGML, TogetherLLM, AnthropicLLM, @@ -39,12 +38,13 @@ MODEL_CLASSES = { Ollama, LlamaCpp, HuggingFaceInferenceAPI, + HuggingFaceTGI, ] } MODEL_MODULE_NAMES = { "OpenAI": "openai", - "MaybeProxyOpenAI": "maybe_proxy_openai", + "OpenAIFreeTrial": "openai_free_trial", "GGML": "ggml", "TogetherLLM": "together", "AnthropicLLM": "anthropic", @@ -52,6 +52,7 @@ MODEL_MODULE_NAMES = { "Ollama": "ollama", "LlamaCpp": "llamacpp", "HuggingFaceInferenceAPI": "hf_inference_api", + "HuggingFaceTGI": "hf_tgi", } @@ -59,13 +60,11 @@ class Models(BaseModel): """Main class that holds the current model configuration""" default: LLM - small: Optional[LLM] = None - medium: Optional[LLM] = None - large: Optional[LLM] = None + summarize: Optional[LLM] = None edit: Optional[LLM] = None chat: Optional[LLM] = None - unused: List[LLM] = [] + saved: List[LLM] = [] # TODO namespace these away to not confuse readers, # or split Models into ModelsConfig, which gets turned into Models @@ -89,7 +88,8 @@ class Models(BaseModel): def set_system_message(self, msg: str): for model in self.all_models: - model.system_message = msg + if model.system_message is None: + model.system_message = msg async def start(self, sdk: "ContinueSDK"): """Start each of the LLMs, or fall back to default""" diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 12fce1c6..64fd784c 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -104,7 +104,7 @@ class ContinueSDK(AbstractContinueSDK): ) await sdk.lsp.start() except Exception as e: - logger.warning(f"Failed to start LSP client: {e}", exc_info=True) + logger.warning(f"Failed to start LSP client: {e}", exc_info=False) sdk.lsp = None create_async_task( |