summaryrefslogtreecommitdiff
path: root/continuedev/src/continuedev/server
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/server
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/server')
-rw-r--r--continuedev/src/continuedev/server/gui.py88
-rw-r--r--continuedev/src/continuedev/server/ide.py28
-rw-r--r--continuedev/src/continuedev/server/ide_protocol.py8
-rw-r--r--continuedev/src/continuedev/server/meilisearch_server.py2
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: