summaryrefslogtreecommitdiff
path: root/continuedev/src/continuedev/core
diff options
context:
space:
mode:
authorNate Sesti <33237525+sestinj@users.noreply.github.com>2023-09-23 13:06:00 -0700
committerGitHub <noreply@github.com>2023-09-23 13:06:00 -0700
commite976d60974a7837967d03807605cbf2e7b4f3f9a (patch)
tree5ecb19062abb162832530dd953e9d2801026c23c /continuedev/src/continuedev/core
parent470711d25b44d1a545c57bc17d40d5e1fd402216 (diff)
downloadsncontinue-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.py124
-rw-r--r--continuedev/src/continuedev/core/config.py6
-rw-r--r--continuedev/src/continuedev/core/main.py6
-rw-r--r--continuedev/src/continuedev/core/models.py24
-rw-r--r--continuedev/src/continuedev/core/sdk.py2
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(