From c4ab2657c07b23d2c62acde7829ad33f98c8cd7f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 3 Jul 2023 22:18:14 -0700 Subject: slash commands and better designed user input box --- continuedev/src/continuedev/core/autopilot.py | 11 +++-- continuedev/src/continuedev/core/config.py | 6 +++ continuedev/src/continuedev/core/main.py | 5 ++ continuedev/src/continuedev/server/gui.py | 9 +--- continuedev/src/continuedev/steps/chat.py | 4 +- continuedev/src/continuedev/steps/core/core.py | 4 +- .../react-app/src/components/StepContainer.tsx | 31 ++++++------ .../src/components/UserInputContainer.tsx | 57 ++++++++++++++++++++++ extension/react-app/src/components/index.ts | 2 +- extension/react-app/src/tabs/gui.tsx | 33 +++++++------ 10 files changed, 116 insertions(+), 46 deletions(-) create mode 100644 extension/react-app/src/components/UserInputContainer.tsx diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 1a77ca64..b3b98b8b 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -66,11 +66,16 @@ class Autopilot(ContinueBaseModel): active=self._active, user_input_queue=self._main_user_input_queue, default_model=self.continue_sdk.config.default_model, - highlighted_ranges=self._highlighted_ranges + highlighted_ranges=self._highlighted_ranges, + slash_commands=self.get_available_slash_commands() ) - async def get_available_slash_commands(self) -> List[Dict]: - return list(map(lambda x: {"name": x.name, "description": x.description}, self.continue_sdk.config.slash_commands)) or [] + def get_available_slash_commands(self) -> List[Dict]: + custom_commands = list(map(lambda x: { + "name": x.name, "description": x.prompt}, self.continue_sdk.config.custom_commands)) or [] + slash_commands = list(map(lambda x: { + "name": x.name, "description": x.description}, self.continue_sdk.config.slash_commands)) or [] + return custom_commands + slash_commands async def change_default_model(self, model: str): self.continue_sdk.update_default_model(model) diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index ed5d785a..d268d247 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -12,6 +12,11 @@ class SlashCommand(BaseModel): params: Optional[Dict] = {} +class CustomCommand(BaseModel): + name: str + prompt: str + + class OnTracebackSteps(BaseModel): step_name: str params: Optional[Dict] = {} @@ -27,6 +32,7 @@ class ContinueConfig(BaseModel): allow_anonymous_telemetry: Optional[bool] = True default_model: Literal["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4"] = 'gpt-4' + custom_commands: Optional[List[CustomCommand]] = [] slash_commands: Optional[List[SlashCommand]] = [ # SlashCommand( # name="pytest", diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 7dff6f53..9a6617f4 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -196,6 +196,10 @@ class History(ContinueBaseModel): return cls(timeline=[], current_index=-1) +class SlashCommandDescription(ContinueBaseModel): + name: str + description: str + class FullState(ContinueBaseModel): """A full state of the program, including the history""" history: History @@ -203,6 +207,7 @@ class FullState(ContinueBaseModel): user_input_queue: List[str] default_model: str highlighted_ranges: List[RangeInFileWithContents] + slash_commands: List[SlashCommandDescription] class ContinueSDK: diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 9a33fb6c..b2f23bac 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -94,12 +94,6 @@ class GUIProtocolServer(AbstractGUIProtocolServer): "state": state }) - async def send_available_slash_commands(self): - commands = await self.session.autopilot.get_available_slash_commands() - await self._send_json("available_slash_commands", { - "commands": commands - }) - def on_main_input(self, input: str): # Do something with user input asyncio.create_task(self.session.autopilot.accept_user_input(input)) @@ -147,8 +141,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we protocol.websocket = websocket # Update any history that may have happened before connection - await protocol.send_available_slash_commands() - # await protocol.send_state_update() THIS WAS CAUSING A LOT OF ISSUES. Don't uncomment. + await protocol.send_state_update() while AppStatus.should_exit is False: message = await websocket.receive_text() diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 013cb578..49dd98e4 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -22,9 +22,9 @@ class SimpleChatStep(Step): user_input: str name: str = "Chat" manage_own_chat_context: bool = True + description: str = "" async def run(self, sdk: ContinueSDK): - self.description = f"`{self.user_input}`\n\n" if self.user_input.strip() == "": self.user_input = "Explain this code's function is a concise list of markdown bullets." self.description = "" @@ -148,9 +148,9 @@ class ChatWithFunctions(Step): DeleteFileStep(filename=""), RunTerminalCommandStep(command="")] name: str = "Input" manage_own_chat_context: bool = True + description: str = "" async def run(self, sdk: ContinueSDK): - self.description = f"```{self.user_input}```" await sdk.update_ui() step_name_step_class_map = { diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 4eb2445c..4ad47689 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -116,6 +116,7 @@ class DefaultModelEditCodeStep(Step): range_in_files: List[RangeInFile] name: str = "Editing Code" hide = False + description: str = "" _prompt: str = dedent("""\ Take the file prefix and suffix into account, but only rewrite the code_to_edit as specified in the user_request. The code you write in modified_code_to_edit will replace the code between the code_to_edit tags. Do NOT preface your answer or write anything other than code. The tag should be written to indicate the end of the modified code section. Do not ever use nested tags. @@ -508,7 +509,6 @@ class DefaultModelEditCodeStep(Step): self._prompt_and_completion += prompt + completion async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self.description = f"`{self.user_input}`" await sdk.update_ui() rif_with_contents = [] @@ -589,7 +589,7 @@ class ManualEditStep(ReversibleStep): class UserInputStep(Step): user_input: str name: str = "User Input" - hide: bool = True + hide: bool = False manage_own_chat_context: bool = True diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index cb83f20a..ab0d307f 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -22,7 +22,6 @@ interface StepContainerProps { historyNode: HistoryNode; onReverse: () => void; inFuture: boolean; - onRefinement: (input: string) => void; onUserInput: (input: string) => void; onRetry: () => void; onDelete: () => void; @@ -33,6 +32,8 @@ interface StepContainerProps { isLast: boolean; } +// #region styled components + const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>` opacity: ${(props) => (props.inFuture ? 0.3 : 1)}; animation: ${appear} 0.3s ease-in-out; @@ -63,9 +64,12 @@ const HeaderDiv = styled.div<{ error: boolean; loading: boolean }>` padding-right: 8px; `; -const ContentDiv = styled.div` +const ContentDiv = styled.div<{ isUserInput: boolean }>` padding: 8px; padding-left: 16px; + background-color: ${(props) => + props.isUserInput ? secondaryDark : vscBackground}; + font-size: 13px; `; const MarkdownPre = styled.pre` @@ -119,10 +123,13 @@ const GradientBorder = styled.div<{ background-size: 200% 200%; `; +// #endregion + function StepContainer(props: StepContainerProps) { const [isHovered, setIsHovered] = useState(false); const naturalLanguageInputRef = useRef(null); const userInputRef = useRef(null); + const isUserInput = props.historyNode.step.name === "UserInputStep"; useEffect(() => { if (userInputRef?.current) { @@ -136,13 +143,6 @@ function StepContainer(props: StepContainerProps) { } }, [isHovered]); - const onTextInput = useCallback(() => { - if (naturalLanguageInputRef.current) { - props.onRefinement(naturalLanguageInputRef.current.value); - naturalLanguageInputRef.current.value = ""; - } - }, [naturalLanguageInputRef]); - return (

- {props.open ? ( - - ) : ( - - )} + {!isUserInput && + (props.open ? ( + + ) : ( + + ))} {props.historyNode.observation?.title || (props.historyNode.step.name as any)}

@@ -225,7 +226,7 @@ function StepContainer(props: StepContainerProps) { -