From dd5b9f6b7f08f178d6034a57f97faea38442eb0a Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 31 May 2023 16:13:01 -0400 Subject: checkpoint! protocol reform and it works now --- extension/src/util/messenger.ts | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 extension/src/util/messenger.ts (limited to 'extension/src/util/messenger.ts') diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts new file mode 100644 index 00000000..6f8bb29d --- /dev/null +++ b/extension/src/util/messenger.ts @@ -0,0 +1,108 @@ +console.log("Websocket import"); +const WebSocket = require("ws"); + +export abstract class Messenger { + abstract send(messageType: string, data: object): void; + + abstract onMessageType( + messageType: string, + callback: (data: object) => void + ): void; + + abstract onMessage(callback: (messageType: string, data: any) => void): void; + + abstract onOpen(callback: () => void): void; + + abstract onClose(callback: () => void): void; + + abstract sendAndReceive(messageType: string, data: any): Promise; +} + +export class WebsocketMessenger extends Messenger { + websocket: WebSocket; + private onMessageListeners: { + [messageType: string]: ((data: object) => void)[]; + } = {}; + private onOpenListeners: (() => void)[] = []; + private onCloseListeners: (() => void)[] = []; + private serverUrl: string; + + _newWebsocket(): WebSocket { + // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser. + // if (typeof process === "object") { + // console.log("Using node"); + // // process is only available in Node + // var WebSocket = require("ws"); + // } + + const newWebsocket = new WebSocket(this.serverUrl); + for (const listener of this.onOpenListeners) { + this.onOpen(listener); + } + for (const listener of this.onCloseListeners) { + this.onClose(listener); + } + for (const messageType in this.onMessageListeners) { + for (const listener of this.onMessageListeners[messageType]) { + this.onMessageType(messageType, listener); + } + } + return newWebsocket; + } + + constructor(serverUrl: string) { + super(); + this.serverUrl = serverUrl; + this.websocket = this._newWebsocket(); + } + + send(messageType: string, data: object) { + const payload = JSON.stringify({ messageType, data }); + if (this.websocket.readyState === this.websocket.OPEN) { + this.websocket.send(payload); + } else { + if (this.websocket.readyState !== this.websocket.CONNECTING) { + this.websocket = this._newWebsocket(); + } + this.websocket.addEventListener("open", () => { + this.websocket.send(payload); + }); + } + } + + sendAndReceive(messageType: string, data: any): Promise { + return new Promise((resolve, reject) => { + const eventListener = (data: any) => { + // THIS ISN"T GETTING CALLED + resolve(data); + this.websocket.removeEventListener("message", eventListener); + }; + this.onMessageType(messageType, eventListener); + this.send(messageType, data); + }); + } + + onMessageType(messageType: string, callback: (data: any) => void): void { + this.websocket.addEventListener("message", (event: any) => { + const msg = JSON.parse(event.data); + if (msg.messageType === messageType) { + callback(msg.data); + } + }); + } + + onMessage(callback: (messageType: string, data: any) => void): void { + this.websocket.addEventListener("message", (event) => { + const msg = JSON.parse(event.data); + callback(msg.messageType, msg.data); + }); + } + + onOpen(callback: () => void): void { + this.websocket.addEventListener("open", callback); + } + + onClose(callback: () => void): void { + this.websocket.addEventListener("close", callback); + } +} -- cgit v1.2.3-70-g09d2 From 43dc3459997d79d112a2776a8e95546580112460 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 29 Jun 2023 09:51:24 -0700 Subject: messaging of welcome step --- extension/media/walkthrough.md | 1 + extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/src/tabs/gui.tsx | 28 ++++++++++++++++++++++------ extension/src/activation/environmentSetup.ts | 9 ++++----- extension/src/continueIdeClient.ts | 15 ++++++++++++--- extension/src/util/messenger.ts | 8 ++++++++ 7 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 extension/media/walkthrough.md (limited to 'extension/src/util/messenger.ts') diff --git a/extension/media/walkthrough.md b/extension/media/walkthrough.md new file mode 100644 index 00000000..fe77a69a --- /dev/null +++ b/extension/media/walkthrough.md @@ -0,0 +1 @@ +# Welcome to Continue diff --git a/extension/package-lock.json b/extension/package-lock.json index 241ea5ca..c33567c4 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.86", + "version": "0.0.88", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.86", + "version": "0.0.88", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 7eca6042..e34af438 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Accelerating software development with language models", - "version": "0.0.86", + "version": "0.0.88", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 249d9785..8d21a457 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -63,7 +63,12 @@ function GUI(props: GUIProps) { >([]); const [dataSwitchChecked, setDataSwitchChecked] = useState(false); const [showDataSharingInfo, setShowDataSharingInfo] = useState(false); - const [stepsOpen, setStepsOpen] = useState([]); + const [stepsOpen, setStepsOpen] = useState([ + true, + true, + true, + true, + ]); const [history, setHistory] = useState({ timeline: [ { @@ -78,13 +83,24 @@ function GUI(props: GUIProps) { { name: "Welcome to Continue", hide: false, - description: - "Type '/' to see the list of available slash commands. If you highlight code, edits and explanations will be localized to the highlighted range. Otherwise, the currently open file is used. In both cases, the code is combined with the previous steps to construct the context.", + description: `Welcome to Continue`, system_message: null, chat_context: [], manage_own_chat_context: false, - message: - "Type '/' to see the list of available slash commands. If you highlight code, edits and explanations will be localized to the highlighted range. Otherwise, the currently open file is used. In both cases, the code is combined with the previous steps to construct the context.", + message: `# Welcome to Continue + + _If it's your first time using Continue, it can take up to a minute for the server to install._ + + Continue is not perfect, but a great tool to add to your toolbox. These are the tasks that Continue is currently best at: + + - Highlight a section of code and instruct Continue to refactor it, e.g. \`"/edit make this use more descriptive variable names"\` + - Ask questions of the open file, e.g. \`"/explain what is the purpose of each of these if statements?"\` + - Ask Continue to build the scaffolding of a new file from scratch, e.g. \`"add a React component for syntax highlighted code"\` + + You can use "slash commands" to directly instruct Continue what to do, or just enter a request and it will automatically decide next steps. To see the list of available slash commands, type '/'. + + If you highlight code, edits and explanations will be localized to the highlighted range. Otherwise, the currently open file is used. In both cases, the code is combined with the previous steps to construct the context. + `, }, { name: "Welcome to Continue!", @@ -585,7 +601,7 @@ function GUI(props: GUIProps) { onClick={() => { client?.sendClear(); }} - text="Clear All" + text="Clear" > diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index 823670fd..b8c23733 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -150,11 +150,10 @@ async function setupPythonEnv() { // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) while (true) { const [, stderr] = await runCommand(createEnvCommand); - if (stderr) { - throw new Error(stderr); - } if (checkEnvExists()) { break; + } else if (stderr) { + throw new Error(stderr); } else { // Remove the env and try again const removeCommand = `rm -rf "${path.join( @@ -180,7 +179,6 @@ async function setupPythonEnv() { activateCmd, pipUpgradeCmd, `${pipCmd} install -r requirements.txt`, - touchCmd, ].join(" ; "); const [, stderr] = await runCommand(installRequirementsCommand); if (stderr) { @@ -273,13 +271,14 @@ export async function startContinuePythonServer() { console.log(`stdout: ${data}`); }); child.stderr.on("data", (data: any) => { - console.log(`stderr: ${data}`); if ( data.includes("Uvicorn running on") || // Successfully started the server data.includes("address already in use") // The server is already running (probably a simultaneously opened VS Code window) ) { console.log("Successfully started Continue python server"); resolve(null); + } else { + console.log(`stderr: ${data}`); } }); child.on("error", (error: any) => { diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 8ab3e075..d983d93a 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -226,9 +226,18 @@ class IdeProtocolClient { } async getSessionId(): Promise { - if (this.messenger === null) { - console.log("MESSENGER IS NULL"); - } + await new Promise((resolve, reject) => { + // Repeatedly try to connect to the server + const interval = setInterval(() => { + if ( + this.messenger && + this.messenger.websocket.readyState === 1 // 1 => OPEN + ) { + clearInterval(interval); + resolve(null); + } + }, 1000); + }); const resp = await this.messenger?.sendAndReceive("openGUI", {}); const sessionId = resp.sessionId; console.log("New Continue session with ID: ", sessionId); diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index 6f8bb29d..e4133230 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -54,6 +54,14 @@ export class WebsocketMessenger extends Messenger { super(); this.serverUrl = serverUrl; this.websocket = this._newWebsocket(); + + const interval = setInterval(() => { + if (this.websocket.readyState === this.websocket.OPEN) { + clearInterval(interval); + } else if (this.websocket.readyState !== this.websocket.CONNECTING) { + this.websocket = this._newWebsocket(); + } + }, 1000); } send(messageType: string, data: object) { -- cgit v1.2.3-70-g09d2 From 81a5ed86119b81a07daf31144e8f09fe6b66246d Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 30 Jun 2023 17:25:35 -0700 Subject: the fix. and others. --- continuedev/pyproject.toml | 2 +- continuedev/src/continuedev/server/ide.py | 6 +-- continuedev/src/continuedev/steps/chat.py | 5 ++- continuedev/src/continuedev/steps/core/core.py | 41 ++++++++++++++++--- extension/.gitignore | 3 +- extension/package-lock.json | 4 +- extension/package.json | 4 +- extension/scripts/requirements.txt | 2 +- extension/src/activation/activate.ts | 32 +++++++-------- extension/src/continueIdeClient.ts | 28 ++++++++----- extension/src/debugPanel.ts | 13 ++++-- extension/src/lang-server/codeLens.ts | 19 ++++----- extension/src/suggestions.ts | 50 +++++++---------------- extension/src/util/messenger.ts | 56 +++++++++++++++++++++----- 14 files changed, 161 insertions(+), 104 deletions(-) (limited to 'extension/src/util/messenger.ts') diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 64d88b8c..e33627e7 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "continuedev" -version = "0.1.1" +version = "0.1.2" description = "" authors = ["Nate Sesti "] readme = "README.md" diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 3c3555f1..e1f19447 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -110,7 +110,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): session_manager: SessionManager sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() - def __init__(self, session_manager: SessionManager): + def __init__(self, session_manager: SessionManager, websocket: WebSocket): + self.websocket = websocket self.session_manager = session_manager async def _send_json(self, message_type: str, data: Any): @@ -354,8 +355,7 @@ async def websocket_endpoint(websocket: WebSocket): print("Accepted websocket connection from, ", websocket.client) await websocket.send_json({"messageType": "connected", "data": {}}) - ideProtocolServer = IdeProtocolServer(session_manager) - ideProtocolServer.websocket = websocket + ideProtocolServer = IdeProtocolServer(session_manager, websocket) 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 6a2c136e..8494563b 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -23,7 +23,10 @@ class SimpleChatStep(Step): name: str = "Chat" async def run(self, sdk: ContinueSDK): - self.description = f"```{self.user_input}```\n\n" + 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 = "" await sdk.update_ui() async for chunk in sdk.models.default.stream_complete(self.user_input, with_history=await sdk.get_chat_context()): diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index dfc7d309..f81b3f6d 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -158,6 +158,14 @@ class DefaultModelEditCodeStep(Step): description = await models.gpt3516k.complete( f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points. Be concise and only mention changes made to the commit before, not prefix or suffix:") self.name = await models.gpt3516k.complete(f"Write a very short title to describe this requested change: '{self.user_input}'. This is the title:") + + # Remove quotes from title and description if they are wrapped + if description.startswith('"') and description.endswith('"'): + description = description[1:-1] + + if self.name.startswith('"') and self.name.endswith('"'): + self.name = self.name[1:-1] + return f"`{self.user_input}`\n\n" + description async def get_prompt_parts(self, rif: RangeInFileWithContents, sdk: ContinueSDK, full_file_contents: str): @@ -170,6 +178,10 @@ class DefaultModelEditCodeStep(Step): total_tokens = model_to_use.count_tokens( full_file_contents + self._prompt + self.user_input) + BUFFER_FOR_FUNCTIONS + DEFAULT_MAX_TOKENS + TOKENS_TO_BE_CONSIDERED_LARGE_RANGE = 1000 + if model_to_use.count_tokens(rif.contents) > TOKENS_TO_BE_CONSIDERED_LARGE_RANGE: + self.description += "\n\n**It looks like you've selected a large range to edit, which may take a while to complete. If you'd like to cancel, click the 'X' button above. If you highlight a more specific range, Continue will only edit within it.**" + # If using 3.5 and overflows, upgrade to 3.5.16k if model_to_use.name == "gpt-3.5-turbo": if total_tokens > MAX_TOKENS_FOR_MODEL["gpt-3.5-turbo"]: @@ -267,8 +279,8 @@ class DefaultModelEditCodeStep(Step): file_prefix, contents, file_suffix, model_to_use = await self.get_prompt_parts( rif, sdk, full_file_contents) - # contents, common_whitespace = dedent_and_get_common_whitespace( - # contents) + contents, common_whitespace = dedent_and_get_common_whitespace( + contents) prompt = self.compile_prompt(file_prefix, contents, file_suffix, sdk) full_file_contents_lines = full_file_contents.split("\n") @@ -304,7 +316,7 @@ class DefaultModelEditCodeStep(Step): if len(current_block_lines) == 0: # Set this as the start of the next block current_block_start = rif.range.start.line + len(original_lines) - len( - original_lines_below_previous_blocks) + offset_from_blocks # current_line_in_file + original_lines_below_previous_blocks) + offset_from_blocks if len(original_lines_below_previous_blocks) > 0 and line == original_lines_below_previous_blocks[0]: # Line is equal to the next line in file, move past this line original_lines_below_previous_blocks = original_lines_below_previous_blocks[ @@ -335,12 +347,23 @@ class DefaultModelEditCodeStep(Step): lines_stripped.append(current_block_lines.pop()) index_of_last_line_in_block -= 1 + # It's also possible that some lines match at the beginning of the block + # lines_stripped_at_beginning = [] + # j = 0 + # while len(current_block_lines) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[first_valid_match[0] - first_valid_match[1] + j]: + # lines_stripped_at_beginning.append( + # current_block_lines.pop(0)) + # j += 1 + # # current_block_start += 1 + # Insert the suggestion replacement = "\n".join(current_block_lines) + start_line = current_block_start + 1 + end_line = current_block_start + index_of_last_line_in_block await sdk.ide.showSuggestion(FileEdit( filepath=rif.filepath, range=Range.from_shorthand( - current_block_start + 1, 0, current_block_start + index_of_last_line_in_block, 0), + start_line, 0, end_line, 0), replacement=replacement )) if replacement == "": @@ -411,7 +434,7 @@ class DefaultModelEditCodeStep(Step): line = line.rstrip() # Add the common whitespace that was removed before prompting - # line = common_whitespace + line + line = common_whitespace + line # Lines that should signify the end of generation if self.is_end_line(line): @@ -437,7 +460,7 @@ class DefaultModelEditCodeStep(Step): # Add the unfinished line if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line, completion_lines_covered == 0) and not self.is_end_line(unfinished_line): - # unfinished_line = common_whitespace + unfinished_line + unfinished_line = common_whitespace + unfinished_line lines.append(unfinished_line) await handle_generated_line(unfinished_line) completion_lines_covered += 1 @@ -459,6 +482,12 @@ class DefaultModelEditCodeStep(Step): current_block_lines = current_block_lines[:- num_to_remove] if num_to_remove > 0 else current_block_lines + # It's also possible that some lines match at the beginning of the block + # while len(current_block_lines) > 0 and len(original_lines_below_previous_blocks) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[0]: + # current_block_lines.pop(0) + # original_lines_below_previous_blocks.pop(0) + # current_block_start += 1 + await sdk.ide.showSuggestion(FileEdit( filepath=rif.filepath, range=Range.from_shorthand( diff --git a/extension/.gitignore b/extension/.gitignore index 6d1b35bf..43b10889 100644 --- a/extension/.gitignore +++ b/extension/.gitignore @@ -3,4 +3,5 @@ node_modules/ out/ .vscode-test/ data/ -src/client \ No newline at end of file +src/client +.continue diff --git a/extension/package-lock.json b/extension/package-lock.json index 26e1a631..7565f480 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.91", + "version": "0.0.97", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.91", + "version": "0.0.97", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 314e540f..08f5f081 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.91", + "version": "0.0.97", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -210,7 +210,7 @@ "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js", "package": "cp ./config/prod_config.json ./config/config.json && mkdir -p ./build && vsce package --out ./build && cp ./config/dev_config.json ./config/config.json", - "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.1-py3-none-any.whl ../extension/scripts/continuedev-0.1.1-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package", + "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.2-py3-none-any.whl ../extension/scripts/continuedev-0.1.2-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package", "install-extension": "code --install-extension ./build/continue-0.0.8.vsix", "uninstall": "code --uninstall-extension .continue", "reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension" diff --git a/extension/scripts/requirements.txt b/extension/scripts/requirements.txt index e6ba1e0a..c51c9d73 100644 --- a/extension/scripts/requirements.txt +++ b/extension/scripts/requirements.txt @@ -3,4 +3,4 @@ # typer==0.7.0 # pydantic # pytest -./continuedev-0.1.1-py3-none-any.whl \ No newline at end of file +./continuedev-0.1.2-py3-none-any.whl \ No newline at end of file diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index cd8f0cf3..18650561 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -20,6 +20,21 @@ export async function activateExtension( ) { extensionContext = context; + await new Promise((resolve, reject) => { + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: + "Starting Continue Server... (it may take a minute to download Python packages)", + cancellable: false, + }, + async (progress, token) => { + await startContinuePythonServer(); + resolve(null); + } + ); + }); + sendTelemetryEvent(TelemetryEvent.ExtensionActivated); registerAllCodeLensProviders(context); registerAllCommands(context); @@ -33,7 +48,7 @@ export async function activateExtension( // Setup the left panel (async () => { - const sessionIdPromise = ideProtocolClient.getSessionId(); + const sessionIdPromise = await ideProtocolClient.getSessionId(); const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); context.subscriptions.push( @@ -46,21 +61,6 @@ export async function activateExtension( ) ); })(); - - await new Promise((resolve, reject) => { - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: - "Starting Continue Server... (it may take a minute to download Python packages)", - cancellable: false, - }, - async (progress, token) => { - await startContinuePythonServer(); - resolve(null); - } - ); - }); // All opened terminals should be replaced by our own terminal // vscode.window.onDidOpenTerminal((terminal) => {}); diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index b179cbf3..21104abe 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -32,8 +32,8 @@ class IdeProtocolClient { messenger.onClose(() => { this.messenger = null; }); - messenger.onMessage((messageType, data) => { - this.handleMessage(messageType, data); + messenger.onMessage((messageType, data, messenger) => { + this.handleMessage(messageType, data, messenger); }); // Setup listeners for any file changes in open editors @@ -67,41 +67,45 @@ class IdeProtocolClient { // }); } - async handleMessage(messageType: string, data: any) { + async handleMessage( + messageType: string, + data: any, + messenger: WebsocketMessenger + ) { switch (messageType) { case "highlightedCode": - this.messenger?.send("highlightedCode", { + messenger.send("highlightedCode", { highlightedCode: this.getHighlightedCode(), }); break; case "workspaceDirectory": - this.messenger?.send("workspaceDirectory", { + messenger.send("workspaceDirectory", { workspaceDirectory: this.getWorkspaceDirectory(), }); break; case "uniqueId": - this.messenger?.send("uniqueId", { + messenger.send("uniqueId", { uniqueId: this.getUniqueId(), }); break; case "getUserSecret": - this.messenger?.send("getUserSecret", { + messenger.send("getUserSecret", { value: await this.getUserSecret(data.key), }); break; case "openFiles": - this.messenger?.send("openFiles", { + messenger.send("openFiles", { openFiles: this.getOpenFiles(), }); break; case "readFile": - this.messenger?.send("readFile", { + messenger.send("readFile", { contents: this.readFile(data.filepath), }); break; case "editFile": const fileEdit = await this.editFile(data.edit); - this.messenger?.send("editFile", { + messenger.send("editFile", { fileEdit, }); break; @@ -109,7 +113,7 @@ class IdeProtocolClient { this.highlightCode(data.rangeInFile, data.color); break; case "runCommand": - this.messenger?.send("runCommand", { + messenger.send("runCommand", { output: await this.runCommand(data.command), }); break; @@ -249,6 +253,8 @@ class IdeProtocolClient { ) { clearInterval(interval); resolve(null); + } else { + console.log("Websocket not yet open, trying again..."); } }, 1000); }); diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index b176eee7..b88c86f3 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -132,7 +132,7 @@ let streamManager = new StreamManager(); export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( panel: vscode.WebviewPanel | vscode.WebviewView, - sessionIdPromise: Promise + sessionIdPromise: Promise | string ): string { debugPanelWebview = panel.webview; panel.onDidDispose(() => { @@ -230,7 +230,12 @@ export function setupDebugPanel( panel.webview.onDidReceiveMessage(async (data) => { switch (data.type) { case "onLoad": { - const sessionId = await sessionIdPromise; + let sessionId: string; + if (typeof sessionIdPromise === "string") { + sessionId = sessionIdPromise; + } else { + sessionId = await sessionIdPromise; + } panel.webview.postMessage({ type: "onLoad", vscMachineId: vscode.env.machineId, @@ -350,9 +355,9 @@ export class ContinueGUIWebviewViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = "continue.continueGUIView"; - private readonly sessionIdPromise: Promise; + private readonly sessionIdPromise: Promise | string; - constructor(sessionIdPromise: Promise) { + constructor(sessionIdPromise: Promise | string) { this.sessionIdPromise = sessionIdPromise; } diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 21448e31..3bd4f153 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -60,18 +60,15 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { } } -const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } = - { - // python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], - "*": [new SuggestionsCodeLensProvider()], - }; +let suggestionsCodeLensDisposable: vscode.Disposable | undefined = undefined; export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { - for (const language in allCodeLensProviders) { - for (const codeLensProvider of allCodeLensProviders[language]) { - context.subscriptions.push( - vscode.languages.registerCodeLensProvider(language, codeLensProvider) - ); - } + if (suggestionsCodeLensDisposable) { + suggestionsCodeLensDisposable.dispose(); } + suggestionsCodeLensDisposable = vscode.languages.registerCodeLensProvider( + "*", + new SuggestionsCodeLensProvider() + ); + context.subscriptions.push(suggestionsCodeLensDisposable); } diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts index e269f38a..baa49711 100644 --- a/extension/src/suggestions.ts +++ b/extension/src/suggestions.ts @@ -4,6 +4,8 @@ import { openEditorAndRevealRange } from "./util/vscode"; import { translate, readFileAtRange } from "./util/vscode"; import * as fs from "fs"; import * as path from "path"; +import { registerAllCodeLensProviders } from "./lang-server/codeLens"; +import { extensionContext } from "./activation/activate"; export interface SuggestionRanges { oldRange: vscode.Range; @@ -125,6 +127,10 @@ export function rerenderDecorations(editorUri: string) { suggestions[idx].newRange, vscode.TextEditorRevealType.Default ); + + if (extensionContext) { + registerAllCodeLensProviders(extensionContext); + } } export function suggestionDownCommand() { @@ -337,42 +343,14 @@ export async function showSuggestion( range: vscode.Range, suggestion: string ): Promise { - // const existingCode = await readFileAtRange( - // new vscode.Range(range.start, range.end), - // editorFilename - // ); - - // If any of the outside lines are the same, don't repeat them in the suggestion - // const slines = suggestion.split("\n"); - // const elines = existingCode.split("\n"); - // let linesRemovedBefore = 0; - // let linesRemovedAfter = 0; - // while (slines.length > 0 && elines.length > 0 && slines[0] === elines[0]) { - // slines.shift(); - // elines.shift(); - // linesRemovedBefore++; - // } - - // while ( - // slines.length > 0 && - // elines.length > 0 && - // slines[slines.length - 1] === elines[elines.length - 1] - // ) { - // slines.pop(); - // elines.pop(); - // linesRemovedAfter++; - // } - - // suggestion = slines.join("\n"); - // if (suggestion === "") return Promise.resolve(false); // Don't even make a suggestion if they are exactly the same - - // range = new vscode.Range( - // new vscode.Position(range.start.line + linesRemovedBefore, 0), - // new vscode.Position( - // range.end.line - linesRemovedAfter, - // elines.at(-1)?.length || 0 - // ) - // ); + // Check for empty suggestions: + if ( + suggestion === "" && + range.start.line === range.end.line && + range.start.character === range.end.character + ) { + return Promise.resolve(false); + } const editor = await openEditorAndRevealRange(editorFilename, range); if (!editor) return Promise.resolve(false); diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index e4133230..b1df161b 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -1,5 +1,6 @@ console.log("Websocket import"); const WebSocket = require("ws"); +import fetch from "node-fetch"; export abstract class Messenger { abstract send(messageType: string, data: object): void; @@ -50,18 +51,49 @@ export class WebsocketMessenger extends Messenger { return newWebsocket; } + async checkServerRunning(serverUrl: string): Promise { + // Check if already running by calling /health + try { + const response = await fetch(serverUrl + "/health"); + if (response.status === 200) { + console.log("Continue python server already running"); + return true; + } else { + return false; + } + } catch (e) { + return false; + } + } + constructor(serverUrl: string) { super(); this.serverUrl = serverUrl; this.websocket = this._newWebsocket(); - const interval = setInterval(() => { - if (this.websocket.readyState === this.websocket.OPEN) { - clearInterval(interval); - } else if (this.websocket.readyState !== this.websocket.CONNECTING) { - this.websocket = this._newWebsocket(); - } - }, 1000); + // Wait until the server is running + // const interval = setInterval(async () => { + // if ( + // await this.checkServerRunning( + // serverUrl.replace("/ide/ws", "").replace("ws://", "http://") + // ) + // ) { + // this.websocket = this._newWebsocket(); + // clearInterval(interval); + // } else { + // console.log( + // "Waiting for python server to start-----------------------" + // ); + // } + // }, 1000); + + // const interval = setInterval(() => { + // if (this.websocket.readyState === this.websocket.OPEN) { + // clearInterval(interval); + // } else if (this.websocket.readyState !== this.websocket.CONNECTING) { + // this.websocket = this._newWebsocket(); + // } + // }, 1000); } send(messageType: string, data: object) { @@ -99,10 +131,16 @@ export class WebsocketMessenger extends Messenger { }); } - onMessage(callback: (messageType: string, data: any) => void): void { + onMessage( + callback: ( + messageType: string, + data: any, + messenger: WebsocketMessenger + ) => void + ): void { this.websocket.addEventListener("message", (event) => { const msg = JSON.parse(event.data); - callback(msg.messageType, msg.data); + callback(msg.messageType, msg.data, this); }); } -- cgit v1.2.3-70-g09d2 From 391764f1371dab06af30a29e10a826a516b69bb3 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 12 Jul 2023 21:53:06 -0700 Subject: persist state and reconnect automatically --- continuedev/src/continuedev/core/autopilot.py | 17 +++- continuedev/src/continuedev/libs/constants/main.py | 6 ++ continuedev/src/continuedev/libs/util/paths.py | 17 ++++ continuedev/src/continuedev/server/gui.py | 10 +- continuedev/src/continuedev/server/ide.py | 25 +++-- continuedev/src/continuedev/server/ide_protocol.py | 10 +- continuedev/src/continuedev/server/main.py | 16 ++- .../src/continuedev/server/session_manager.py | 41 ++++++-- docs/docs/concepts/ide.md | 4 +- extension/react-app/src/components/ComboBox.tsx | 1 - extension/react-app/src/hooks/messenger.ts | 10 -- extension/react-app/src/pages/gui.tsx | 1 - extension/src/activation/activate.ts | 2 +- extension/src/continueIdeClient.ts | 62 ++++++++--- extension/src/debugPanel.ts | 113 ++++----------------- extension/src/util/messenger.ts | 10 ++ 16 files changed, 192 insertions(+), 153 deletions(-) create mode 100644 continuedev/src/continuedev/libs/constants/main.py create mode 100644 continuedev/src/continuedev/libs/util/paths.py (limited to 'extension/src/util/messenger.ts') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 1b074435..e1c8a076 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -1,13 +1,13 @@ from functools import cached_property import traceback import time -from typing import Any, Callable, Coroutine, Dict, List +from typing import Any, Callable, Coroutine, Dict, List, Union import os from aiohttp import ClientPayloadError +from pydantic import root_validator from ..models.filesystem import RangeInFileWithContents from ..models.filesystem_edit import FileEditWithFullContents -from ..libs.llm import LLM from .observation import Observation, InternalErrorObservation from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue @@ -16,7 +16,6 @@ from .main import Context, ContinueCustomException, HighlightedRangeContext, Pol from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK -import asyncio from ..libs.util.step_name_to_steps import get_step_from_name from ..libs.util.traceback_parsers import get_python_traceback, get_javascript_traceback from openai import error as openai_errors @@ -46,6 +45,7 @@ class Autopilot(ContinueBaseModel): ide: AbstractIdeProtocolServer history: History = History.from_empty() context: Context = Context() + full_state: Union[FullState, None] = None _on_update_callbacks: List[Callable[[FullState], None]] = [] _active: bool = False @@ -63,8 +63,15 @@ class Autopilot(ContinueBaseModel): arbitrary_types_allowed = True keep_untouched = (cached_property,) + @root_validator(pre=True) + def fill_in_values(cls, values): + full_state: FullState = values.get('full_state') + if full_state is not None: + values['history'] = full_state.history + return values + def get_full_state(self) -> FullState: - return FullState( + full_state = FullState( history=self.history, active=self._active, user_input_queue=self._main_user_input_queue, @@ -73,6 +80,8 @@ class Autopilot(ContinueBaseModel): slash_commands=self.get_available_slash_commands(), adding_highlighted_code=self._adding_highlighted_code, ) + self.full_state = full_state + return full_state def get_available_slash_commands(self) -> List[Dict]: custom_commands = list(map(lambda x: { diff --git a/continuedev/src/continuedev/libs/constants/main.py b/continuedev/src/continuedev/libs/constants/main.py new file mode 100644 index 00000000..96eb6e69 --- /dev/null +++ b/continuedev/src/continuedev/libs/constants/main.py @@ -0,0 +1,6 @@ +## PATHS ## + +CONTINUE_GLOBAL_FOLDER = ".continue" +CONTINUE_SESSIONS_FOLDER = "sessions" +CONTINUE_SERVER_FOLDER = "server" + diff --git a/continuedev/src/continuedev/libs/util/paths.py b/continuedev/src/continuedev/libs/util/paths.py new file mode 100644 index 00000000..fddef887 --- /dev/null +++ b/continuedev/src/continuedev/libs/util/paths.py @@ -0,0 +1,17 @@ +import os + +from ..constants.main import CONTINUE_SESSIONS_FOLDER, CONTINUE_GLOBAL_FOLDER, CONTINUE_SERVER_FOLDER + +def getGlobalFolderPath(): + return os.path.join(os.path.expanduser("~"), CONTINUE_GLOBAL_FOLDER) + + + +def getSessionsFolderPath(): + return os.path.join(getGlobalFolderPath(), CONTINUE_SESSIONS_FOLDER) + +def getServerFolderPath(): + return os.path.join(getGlobalFolderPath(), CONTINUE_SERVER_FOLDER) + +def getSessionFilePath(session_id: str): + return os.path.join(getSessionsFolderPath(), f"{session_id}.json") \ No newline at end of file diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 21089f30..8f6f68f6 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -31,12 +31,12 @@ class AppStatus: Server.handle_exit = AppStatus.handle_exit -def session(x_continue_session_id: str = Header("anonymous")) -> Session: - return session_manager.get_session(x_continue_session_id) +async def session(x_continue_session_id: str = Header("anonymous")) -> Session: + return await session_manager.get_session(x_continue_session_id) -def websocket_session(session_id: str) -> Session: - return session_manager.get_session(session_id) +async def websocket_session(session_id: str) -> Session: + return await session_manager.get_session(session_id) T = TypeVar("T", bound=BaseModel) @@ -199,4 +199,6 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we print("Closing gui websocket") if websocket.client_state != WebSocketState.DISCONNECTED: await websocket.close() + + session_manager.persist_session(session.session_id) session_manager.remove_session(session.session_id) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 4645b49e..12a21f19 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -52,9 +52,11 @@ class FileEditsUpdate(BaseModel): class OpenFilesResponse(BaseModel): openFiles: List[str] + class VisibleFilesResponse(BaseModel): visibleFiles: List[str] + class HighlightedCodeResponse(BaseModel): highlightedCode: List[RangeInFile] @@ -115,6 +117,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): websocket: WebSocket session_manager: SessionManager sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() + session_id: Union[str, None] = None def __init__(self, session_manager: SessionManager, websocket: WebSocket): self.websocket = websocket @@ -132,8 +135,6 @@ class IdeProtocolServer(AbstractIdeProtocolServer): continue message_type = message["messageType"] data = message["data"] - # if message_type == "openGUI": - # await self.openGUI() if message_type == "workspaceDirectory": self.workspace_directory = data["workspaceDirectory"] break @@ -158,8 +159,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return resp_model.parse_obj(resp) async def handle_json(self, message_type: str, data: Any): - if message_type == "openGUI": - await self.openGUI() + if message_type == "getSessionId": + await self.getSessionId() elif message_type == "setFileOpen": await self.setFileOpen(data["filepath"], data["open"]) elif message_type == "setSuggestionsLocked": @@ -217,9 +218,10 @@ class IdeProtocolServer(AbstractIdeProtocolServer): "locked": locked }) - async def openGUI(self): - session_id = self.session_manager.new_session(self) - await self._send_json("openGUI", { + async def getSessionId(self): + session_id = self.session_manager.new_session( + self, self.session_id).session_id + await self._send_json("getSessionId", { "sessionId": session_id }) @@ -304,7 +306,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def getOpenFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, OpenFilesResponse, "openFiles") return resp.openFiles - + async def getVisibleFiles(self) -> List[str]: resp = await self._send_and_receive_json({}, VisibleFilesResponse, "visibleFiles") return resp.visibleFiles @@ -416,7 +418,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): @router.websocket("/ws") -async def websocket_endpoint(websocket: WebSocket): +async def websocket_endpoint(websocket: WebSocket, session_id: str = None): try: await websocket.accept() print("Accepted websocket connection from, ", websocket.client) @@ -434,6 +436,9 @@ async def websocket_endpoint(websocket: WebSocket): ideProtocolServer.handle_json(message_type, data)) ideProtocolServer = IdeProtocolServer(session_manager, websocket) + ideProtocolServer.session_id = session_id + if session_id is not None: + session_manager.registered_ides[session_id] = ideProtocolServer other_msgs = await ideProtocolServer.initialize() for other_msg in other_msgs: @@ -454,3 +459,5 @@ async def websocket_endpoint(websocket: WebSocket): finally: if websocket.client_state != WebSocketState.DISCONNECTED: await websocket.close() + + session_manager.registered_ides.pop(ideProtocolServer.session_id) diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 2783dc61..2f78cf0e 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -1,5 +1,6 @@ -from typing import Any, List +from typing import Any, List, Union from abc import ABC, abstractmethod, abstractproperty +from fastapi import WebSocket from ..models.main import Traceback from ..models.filesystem_edit import FileEdit, FileSystemEdit, EditDiff @@ -7,6 +8,9 @@ from ..models.filesystem import RangeInFile, RangeInFileWithContents class AbstractIdeProtocolServer(ABC): + websocket: WebSocket + session_id: Union[str, None] + @abstractmethod async def handle_json(self, data: Any): """Handle a json message""" @@ -24,8 +28,8 @@ class AbstractIdeProtocolServer(ABC): """Set whether suggestions are locked""" @abstractmethod - async def openGUI(self): - """Open a GUI""" + async def getSessionId(self): + """Get a new session ID""" @abstractmethod async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool: diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py index f4d82903..aa093853 100644 --- a/continuedev/src/continuedev/server/main.py +++ b/continuedev/src/continuedev/server/main.py @@ -4,7 +4,8 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from .ide import router as ide_router from .gui import router as gui_router -import logging +from .session_manager import session_manager +import atexit import uvicorn import argparse @@ -44,5 +45,16 @@ def run_server(): uvicorn.run(app, host="0.0.0.0", port=args.port) +def cleanup(): + print("Cleaning up sessions") + for session_id in session_manager.sessions: + session_manager.persist_session(session_id) + + +atexit.register(cleanup) if __name__ == "__main__": - run_server() + try: + run_server() + except Exception as e: + cleanup() + raise e diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index 7147dcfa..fb8ac386 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -1,9 +1,12 @@ -from asyncio import BaseEventLoop +import os from fastapi import WebSocket from typing import Any, Dict, List, Union from uuid import uuid4 +import json +from ..libs.util.paths import getSessionFilePath, getSessionsFolderPath from ..models.filesystem_edit import FileEditWithFullContents +from ..libs.constants.main import CONTINUE_SESSIONS_FOLDER from ..core.policy import DemoPolicy from ..core.main import FullState from ..core.autopilot import Autopilot @@ -39,17 +42,35 @@ class DemoAutopilot(Autopilot): class SessionManager: sessions: Dict[str, Session] = {} + # Mapping of session_id to IDE, where the IDE is still alive + registered_ides: Dict[str, AbstractIdeProtocolServer] = {} - def get_session(self, session_id: str) -> Session: + async def get_session(self, session_id: str) -> Session: if session_id not in self.sessions: + # Check then whether it is persisted by listing all files in the sessions folder + # And only if the IDE is still alive + sessions_folder = getSessionsFolderPath() + session_files = os.listdir(sessions_folder) + if f"{session_id}.json" in session_files and session_id in self.registered_ides: + if self.registered_ides[session_id].session_id is not None: + return self.new_session(self.registered_ides[session_id], session_id=session_id) + raise KeyError("Session ID not recognized", session_id) return self.sessions[session_id] - def new_session(self, ide: AbstractIdeProtocolServer) -> str: - autopilot = DemoAutopilot(policy=DemoPolicy(), ide=ide) - session_id = str(uuid4()) + def new_session(self, ide: AbstractIdeProtocolServer, session_id: Union[str, None] = None) -> Session: + full_state = None + if session_id is not None and os.path.exists(getSessionFilePath(session_id)): + with open(getSessionFilePath(session_id), "r") as f: + full_state = FullState(**json.load(f)) + + autopilot = DemoAutopilot( + policy=DemoPolicy(), ide=ide, full_state=full_state) + session_id = session_id or str(uuid4()) + ide.session_id = session_id session = Session(session_id=session_id, autopilot=autopilot) self.sessions[session_id] = session + self.registered_ides[session_id] = ide async def on_update(state: FullState): await session_manager.send_ws_data(session_id, "state_update", { @@ -58,11 +79,19 @@ class SessionManager: autopilot.on_update(on_update) create_async_task(autopilot.run_policy()) - return session_id + return session def remove_session(self, session_id: str): del self.sessions[session_id] + def persist_session(self, session_id: str): + """Save the session's FullState as a json file""" + full_state = self.sessions[session_id].autopilot.get_full_state() + if not os.path.exists(getSessionsFolderPath()): + os.mkdir(getSessionsFolderPath()) + with open(getSessionFilePath(session_id), "w") as f: + json.dump(full_state.dict(), f) + def register_websocket(self, session_id: str, ws: WebSocket): self.sessions[session_id].ws = ws print("Registered websocket for session", session_id) diff --git a/docs/docs/concepts/ide.md b/docs/docs/concepts/ide.md index dc7b9e23..bd31481b 100644 --- a/docs/docs/concepts/ide.md +++ b/docs/docs/concepts/ide.md @@ -41,9 +41,9 @@ Get the workspace directory Set whether a file is open -### openGUI +### getSessionId -Open a gui +Get a new session ID ### showSuggestionsAndWait diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index ac994b0a..7d6541c7 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -331,7 +331,6 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { ) { (event.nativeEvent as any).preventDownshiftDefault = true; } else if (event.key === "ArrowUp") { - console.log("OWJFOIJO"); if (positionInHistory == 0) return; else if ( positionInHistory == history.length && diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts index e2a0bab8..00ce1fbb 100644 --- a/extension/react-app/src/hooks/messenger.ts +++ b/extension/react-app/src/hooks/messenger.ts @@ -1,6 +1,3 @@ -// console.log("Websocket import"); -// const WebSocket = require("ws"); - export abstract class Messenger { abstract send(messageType: string, data: object): void; @@ -28,13 +25,6 @@ export class WebsocketMessenger extends Messenger { private serverUrl: string; _newWebsocket(): WebSocket { - // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser. - // if (typeof process === "object") { - // console.log("Using node"); - // // process is only available in Node - // var WebSocket = require("ws"); - // } - const newWebsocket = new WebSocket(this.serverUrl); for (const listener of this.onOpenListeners) { this.onOpen(listener); diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index b9382bd1..4ff260fa 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -262,7 +262,6 @@ function GUI(props: GUIProps) { const onStepUserInput = (input: string, index: number) => { if (!client) return; - console.log("Sending step user input", input, index); client.sendStepUserInput(input, index); }; diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 559caf44..cd885b12 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -56,7 +56,7 @@ export async function activateExtension(context: vscode.ExtensionContext) { registerAllCodeLensProviders(context); registerAllCommands(context); - // Initialize IDE Protocol Client, then call "openGUI" + // Initialize IDE Protocol Client const serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( `${serverUrl.replace("http", "ws")}/ide/ws`, diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index b728833f..4c1fdf1e 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -1,10 +1,9 @@ -// import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest"; import { editorSuggestionsLocked, showSuggestion as showSuggestionInEditor, SuggestionRanges, } from "./suggestions"; -import { openEditorAndRevealRange, getRightViewColumn } from "./util/vscode"; +import { openEditorAndRevealRange } from "./util/vscode"; import { FileEdit } from "../schema/FileEdit"; import { RangeInFile } from "../schema/RangeInFile"; import * as vscode from "vscode"; @@ -15,8 +14,6 @@ import { import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; -import * as path from "path"; -import * as os from "os"; import { diffManager } from "./diffs"; class IdeProtocolClient { @@ -27,17 +24,54 @@ class IdeProtocolClient { private _highlightDebounce: NodeJS.Timeout | null = null; - constructor(serverUrl: string, context: vscode.ExtensionContext) { - this.context = context; + private _lastReloadTime: number = 16; + private _reconnectionTimeouts: NodeJS.Timeout[] = []; + + private _sessionId: string | null = null; + private _serverUrl: string; - let messenger = new WebsocketMessenger(serverUrl); + private _newWebsocketMessenger() { + const requestUrl = + this._serverUrl + + (this._sessionId ? `?session_id=${this._sessionId}` : ""); + const messenger = new WebsocketMessenger(requestUrl); this.messenger = messenger; - messenger.onClose(() => { + + const reconnect = () => { + console.log("Trying to reconnect IDE protocol websocket..."); this.messenger = null; + + // Exponential backoff to reconnect + this._reconnectionTimeouts.forEach((to) => clearTimeout(to)); + + const timeout = setTimeout(() => { + if (this.messenger?.websocket?.readyState === 1) { + return; + } + this._newWebsocketMessenger(); + }, this._lastReloadTime); + + this._reconnectionTimeouts.push(timeout); + this._lastReloadTime = Math.min(2 * this._lastReloadTime, 5000); + }; + messenger.onOpen(() => { + this._reconnectionTimeouts.forEach((to) => clearTimeout(to)); + }); + messenger.onClose(() => { + reconnect(); + }); + messenger.onError(() => { + reconnect(); }); messenger.onMessage((messageType, data, messenger) => { this.handleMessage(messageType, data, messenger); }); + } + + constructor(serverUrl: string, context: vscode.ExtensionContext) { + this.context = context; + this._serverUrl = serverUrl; + this._newWebsocketMessenger(); // Setup listeners for any file changes in open editors // vscode.workspace.onDidChangeTextDocument((event) => { @@ -171,7 +205,7 @@ class IdeProtocolClient { case "showDiff": this.showDiff(data.filepath, data.replacement, data.step_index); break; - case "openGUI": + case "getSessionId": case "connected": break; default: @@ -284,10 +318,6 @@ class IdeProtocolClient { // ------------------------------------ // // Initiate Request - async openGUI(asRightWebviewPanel: boolean = false) { - // Open the webview panel - } - async getSessionId(): Promise { await new Promise((resolve, reject) => { // Repeatedly try to connect to the server @@ -303,10 +333,10 @@ class IdeProtocolClient { } }, 1000); }); - const resp = await this.messenger?.sendAndReceive("openGUI", {}); - const sessionId = resp.sessionId; + const resp = await this.messenger?.sendAndReceive("getSessionId", {}); // console.log("New Continue session with ID: ", sessionId); - return sessionId; + this._sessionId = resp.sessionId; + return resp.sessionId; } acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 487bbedf..5e1689d1 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -8,76 +8,6 @@ import { import { RangeInFile } from "./client"; const WebSocket = require("ws"); -class StreamManager { - private _fullText: string = ""; - private _insertionPoint: vscode.Position | undefined; - - private _addToEditor(update: string) { - let editor = - vscode.window.activeTextEditor || vscode.window.visibleTextEditors[0]; - - if (typeof this._insertionPoint === "undefined") { - if (editor?.selection.isEmpty) { - this._insertionPoint = editor?.selection.active; - } else { - this._insertionPoint = editor?.selection.end; - } - } - editor?.edit((editBuilder) => { - if (this._insertionPoint) { - editBuilder.insert(this._insertionPoint, update); - this._insertionPoint = this._insertionPoint.translate( - Array.from(update.matchAll(/\n/g)).length, - update.length - ); - } - }); - } - - public closeStream() { - this._fullText = ""; - this._insertionPoint = undefined; - this._codeBlockStatus = "closed"; - this._pendingBackticks = 0; - } - - private _codeBlockStatus: "open" | "closed" | "language-descriptor" = - "closed"; - private _pendingBackticks: number = 0; - public onStreamUpdate(update: string) { - let textToInsert = ""; - for (let i = 0; i < update.length; i++) { - switch (this._codeBlockStatus) { - case "closed": - if (update[i] === "`" && this._fullText.endsWith("``")) { - this._codeBlockStatus = "language-descriptor"; - } - break; - case "language-descriptor": - if (update[i] === " " || update[i] === "\n") { - this._codeBlockStatus = "open"; - } - break; - case "open": - if (update[i] === "`") { - if (this._fullText.endsWith("``")) { - this._codeBlockStatus = "closed"; - this._pendingBackticks = 0; - } else { - this._pendingBackticks += 1; - } - } else { - textToInsert += "`".repeat(this._pendingBackticks) + update[i]; - this._pendingBackticks = 0; - } - break; - } - this._fullText += update[i]; - } - this._addToEditor(textToInsert); - } -} - let websocketConnections: { [url: string]: WebsocketConnection | undefined } = {}; @@ -127,8 +57,6 @@ class WebsocketConnection { } } -let streamManager = new StreamManager(); - export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( panel: vscode.WebviewPanel | vscode.WebviewView, @@ -147,10 +75,7 @@ export function setupDebugPanel( .toString(); const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development; - if (!isProduction) { - scriptUri = "http://localhost:5173/src/main.tsx"; - styleMainUri = "http://localhost:5173/src/main.css"; - } else { + if (isProduction) { scriptUri = debugPanelWebview .asWebviewUri( vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.js") @@ -161,6 +86,9 @@ export function setupDebugPanel( vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.css") ) .toString(); + } else { + scriptUri = "http://localhost:5173/src/main.tsx"; + styleMainUri = "http://localhost:5173/src/main.css"; } panel.webview.options = { @@ -175,11 +103,11 @@ export function setupDebugPanel( return; } - let rangeInFile: RangeInFile = { + const rangeInFile: RangeInFile = { range: e.selections[0], filepath: e.textEditor.document.fileName, }; - let filesystem = { + const filesystem = { [rangeInFile.filepath]: e.textEditor.document.getText(), }; panel.webview.postMessage({ @@ -217,13 +145,19 @@ export function setupDebugPanel( url, }); }; - const connection = new WebsocketConnection( - url, - onMessage, - onOpen, - onClose - ); - websocketConnections[url] = connection; + try { + const connection = new WebsocketConnection( + url, + onMessage, + onOpen, + onClose + ); + websocketConnections[url] = connection; + resolve(null); + } catch (e) { + console.log("Caught it!: ", e); + reject(e); + } }); } @@ -292,15 +226,6 @@ export function setupDebugPanel( openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); break; } - case "streamUpdate": { - // Write code at the position of the cursor - streamManager.onStreamUpdate(data.update); - break; - } - case "closeStream": { - streamManager.closeStream(); - break; - } case "withProgress": { // This message allows withProgress to be used in the webview if (data.done) { diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index b1df161b..7fd71ddd 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -15,6 +15,8 @@ export abstract class Messenger { abstract onOpen(callback: () => void): void; abstract onClose(callback: () => void): void; + + abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; } @@ -26,6 +28,7 @@ export class WebsocketMessenger extends Messenger { } = {}; private onOpenListeners: (() => void)[] = []; private onCloseListeners: (() => void)[] = []; + private onErrorListeners: (() => void)[] = []; private serverUrl: string; _newWebsocket(): WebSocket { @@ -43,6 +46,9 @@ export class WebsocketMessenger extends Messenger { for (const listener of this.onCloseListeners) { this.onClose(listener); } + for (const listener of this.onErrorListeners) { + this.onError(listener); + } for (const messageType in this.onMessageListeners) { for (const listener of this.onMessageListeners[messageType]) { this.onMessageType(messageType, listener); @@ -151,4 +157,8 @@ export class WebsocketMessenger extends Messenger { onClose(callback: () => void): void { this.websocket.addEventListener("close", callback); } + + onError(callback: () => void): void { + this.websocket.addEventListener("error", callback); + } } -- cgit v1.2.3-70-g09d2 From c5102b0997baa81ce544514d6b5b4d5a2eae804f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 13:45:10 -0700 Subject: insidious client_state vs application_state err --- continuedev/src/continuedev/core/autopilot.py | 2 ++ continuedev/src/continuedev/server/gui.py | 2 +- continuedev/src/continuedev/server/ide.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 9 ++++++++- extension/react-app/src/components/ComboBox.tsx | 9 ++++++++- extension/react-app/src/components/StepContainer.tsx | 9 ++++++--- extension/src/continueIdeClient.ts | 8 ++++++-- extension/src/diffs.ts | 2 +- extension/src/util/messenger.ts | 2 +- 9 files changed, 34 insertions(+), 11 deletions(-) (limited to 'extension/src/util/messenger.ts') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index e1c8a076..82439f49 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -37,6 +37,8 @@ def get_error_title(e: Exception) -> str: return "The request failed. Please check your internet connection and try again. If this issue persists, you can use our API key for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to \"\"" elif isinstance(e, openai_errors.InvalidRequestError): return 'Your API key does not have access to GPT-4. You can use ours for free by going to VS Code settings and changing the value of continue.OPENAI_API_KEY to ""' + elif e.__str__().startswith("Cannot connect to host"): + return "The request failed. Please check your internet connection and try again." return e.__str__() or e.__repr__() diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 238273b2..9a411fbe 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -53,7 +53,7 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session = session async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 73cce201..7875c94d 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -149,7 +149,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): return other_msgs async def _send_json(self, message_type: str, data: Any): - if self.websocket.client_state == WebSocketState.DISCONNECTED: + if self.websocket.application_state == WebSocketState.DISCONNECTED: return await self.websocket.send_json({ "messageType": message_type, diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 787da316..75f8e460 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -9,7 +9,7 @@ from ...libs.llm.prompt_utils import MarkdownStyleEncoderDecoder from ...models.filesystem_edit import EditDiff, FileEdit, FileEditWithFullContents, FileSystemEdit from ...models.filesystem import FileSystem, RangeInFile, RangeInFileWithContents from ...core.observation import Observation, TextObservation, TracebackObservation, UserInputObservation -from ...core.main import ChatMessage, Step, SequentialStep +from ...core.main import ChatMessage, ContinueCustomException, Step, SequentialStep from ...libs.util.count_tokens import MAX_TOKENS_FOR_MODEL, DEFAULT_MAX_TOKENS from ...libs.util.dedent import dedent_and_get_common_whitespace import difflib @@ -608,6 +608,13 @@ Please output the code to be inserted at the cursor in order to fulfill the user rif_dict[rif.filepath] = rif.contents for rif in rif_with_contents: + # If the file doesn't exist, ask them to save it first + if not os.path.exists(rif.filepath): + message = f"The file {rif.filepath} does not exist. Please save it first." + raise ContinueCustomException( + title=message, message=message + ) + await sdk.ide.setFileOpen(rif.filepath) await sdk.ide.setSuggestionsLocked(rif.filepath, True) await self.stream_rif(rif, sdk) diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 5d9b5109..754c9445 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -169,6 +169,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); const [metaKeyPressed, setMetaKeyPressed] = useState(false); + const [focused, setFocused] = useState(false); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Meta") { @@ -298,7 +299,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { // setShowContextDropdown(target.value.endsWith("@")); }, + onFocus: (e) => { + setFocused(true); + }, onBlur: (e) => { + setFocused(false); postVscMessage("blurContinueInput", {}); }, onKeyDown: (event) => { @@ -374,7 +379,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { {highlightedCodeSections.length === 0 && (downshiftProps.inputValue?.startsWith("/edit") || - (metaKeyPressed && downshiftProps.inputValue?.length > 0)) && ( + (focused && + metaKeyPressed && + downshiftProps.inputValue?.length > 0)) && (
Inserting at cursor
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 6fa4ba13..14e9b854 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -253,9 +253,12 @@ function StepContainer(props: StepContainerProps) { )} {props.historyNode.observation?.error ? ( -
-              {props.historyNode.observation.error as string}
-            
+
+ View Traceback +
+                {props.historyNode.observation.error as string}
+              
+
) : ( void): void; abstract onClose(callback: () => void): void; - + abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; -- cgit v1.2.3-70-g09d2 From 861a873f7ecf455b9c7833040b2a8163e369e062 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 22:26:09 -0700 Subject: checkpoint. Somewhat working, just a bit slow, probably some blocking meilisearch calls still happening --- continuedev/poetry.lock | 105 +++++------------------------- continuedev/pyproject.toml | 1 + continuedev/src/continuedev/server/gui.py | 4 +- continuedev/src/continuedev/server/ide.py | 6 +- extension/react-app/src/main.tsx | 7 +- extension/src/activation/activate.ts | 3 +- extension/src/continueIdeClient.ts | 3 +- extension/src/debugPanel.ts | 2 + extension/src/util/messenger.ts | 9 ++- 9 files changed, 40 insertions(+), 100 deletions(-) (limited to 'extension/src/util/messenger.ts') diff --git a/continuedev/poetry.lock b/continuedev/poetry.lock index b67128fd..6644bf6f 100644 --- a/continuedev/poetry.lock +++ b/continuedev/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.4" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -113,7 +112,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -128,7 +126,6 @@ frozenlist = ">=1.1.0" name = "anthropic" version = "0.3.4" description = "Client library for the anthropic API" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -148,7 +145,6 @@ typing-extensions = ">=4.1.1,<5" name = "anyio" version = "3.6.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.6.2" files = [ @@ -169,7 +165,6 @@ trio = ["trio (>=0.16,<0.22)"] name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -181,7 +176,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -200,7 +194,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -212,7 +205,6 @@ files = [ name = "boltons" version = "23.0.0" description = "When they're not builtins, they're boltons." -category = "main" optional = false python-versions = "*" files = [ @@ -224,7 +216,6 @@ files = [ name = "camel-converter" version = "3.0.2" description = "Converts a string from snake case to camel case or camel case to snake case" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -242,7 +233,6 @@ pydantic = ["pydantic (>=1.8.2)"] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -254,7 +244,6 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -331,7 +320,6 @@ pycparser = "*" name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -416,7 +404,6 @@ files = [ name = "chevron" version = "0.14.0" description = "Mustache templating language renderer" -category = "main" optional = false python-versions = "*" files = [ @@ -428,7 +415,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -443,7 +429,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -455,7 +440,6 @@ files = [ name = "cryptography" version = "41.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -501,7 +485,6 @@ test-randomorder = ["pytest-randomly"] name = "dataclasses-json" version = "0.5.7" description = "Easily serialize dataclasses to and from JSON" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -521,7 +504,6 @@ dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest ( name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -539,7 +521,6 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "diff-match-patch" version = "20230430" description = "Diff Match and Patch" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -554,7 +535,6 @@ dev = ["attribution (==1.6.2)", "black (==23.3.0)", "flit (==3.8.0)", "mypy (==1 name = "directory-tree" version = "0.0.3.1" description = "Utility Package that Displays out the Tree Structure of a Particular Directory." -category = "main" optional = false python-versions = "*" files = [ @@ -569,7 +549,6 @@ dev = ["pytest (>=3.7)"] name = "distro" version = "1.8.0" description = "Distro - an OS platform information API" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -581,7 +560,6 @@ files = [ name = "fastapi" version = "0.95.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -603,7 +581,6 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6 name = "frozenlist" version = "1.3.3" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -683,11 +660,20 @@ files = [ {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, ] +[[package]] +name = "func-timeout" +version = "4.3.5" +description = "Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads" +optional = false +python-versions = "*" +files = [ + {file = "func_timeout-4.3.5.tar.gz", hash = "sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd"}, +] + [[package]] name = "gpt-index" version = "0.6.8" description = "Interface between LLMs and your data" -category = "main" optional = false python-versions = "*" files = [ @@ -709,7 +695,6 @@ tiktoken = "*" name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -783,7 +768,6 @@ test = ["objgraph", "psutil"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -795,7 +779,6 @@ files = [ name = "httpcore" version = "0.17.3" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -807,17 +790,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httpx" version = "0.24.1" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -833,15 +815,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -853,7 +834,6 @@ files = [ name = "importlib-resources" version = "6.0.0" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -872,7 +852,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "jsonref" version = "1.1.0" description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -884,7 +863,6 @@ files = [ name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -906,7 +884,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "langchain" version = "0.0.171" description = "Building applications with LLMs through composability" -category = "main" optional = false python-versions = ">=3.8.1,<4.0" files = [ @@ -943,7 +920,6 @@ qdrant = ["qdrant-client (>=1.1.2,<2.0.0)"] name = "marshmallow" version = "3.19.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -964,7 +940,6 @@ tests = ["pytest", "pytz", "simplejson"] name = "marshmallow-enum" version = "1.5.1" description = "Enum field for Marshmallow" -category = "main" optional = false python-versions = "*" files = [ @@ -979,7 +954,6 @@ marshmallow = ">=2.0.0" name = "meilisearch" version = "0.28.1" description = "The python client for Meilisearch API." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -995,7 +969,6 @@ requests = "*" name = "monotonic" version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" -category = "main" optional = false python-versions = "*" files = [ @@ -1007,7 +980,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1091,7 +1063,6 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1103,7 +1074,6 @@ files = [ name = "nest-asyncio" version = "1.5.6" description = "Patch asyncio to allow nested event loops" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1115,7 +1085,6 @@ files = [ name = "numexpr" version = "2.8.4" description = "Fast numerical expression evaluator for NumPy" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1158,7 +1127,6 @@ numpy = ">=1.13.3" name = "numpy" version = "1.24.3" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1196,7 +1164,6 @@ files = [ name = "openai" version = "0.27.6" description = "Python client library for the OpenAI API" -category = "main" optional = false python-versions = ">=3.7.1" files = [ @@ -1211,7 +1178,7 @@ tqdm = "*" [package.extras] datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] +dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-mock"] embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] @@ -1219,7 +1186,6 @@ wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1 name = "openapi-schema-pydantic" version = "1.2.4" description = "OpenAPI (v3) specification schema as pydantic class" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -1234,7 +1200,6 @@ pydantic = ">=1.8.2" name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1246,7 +1211,6 @@ files = [ name = "pandas" version = "2.0.1" description = "Powerful data structures for data analysis, time series, and statistics" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1314,7 +1278,6 @@ xml = ["lxml (>=4.6.3)"] name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1326,7 +1289,6 @@ files = [ name = "posthog" version = "3.0.1" description = "Integrate PostHog into any python application." -category = "main" optional = false python-versions = "*" files = [ @@ -1350,7 +1312,6 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint" name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1377,7 +1338,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1389,7 +1349,6 @@ files = [ name = "pydantic" version = "1.10.7" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1442,7 +1401,6 @@ email = ["email-validator (>=1.0.3)"] name = "pygithub" version = "1.59.0" description = "Use the full Github API v3" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1460,7 +1418,6 @@ requests = ">=2.14.0" name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1481,7 +1438,6 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1508,7 +1464,6 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1545,7 +1500,6 @@ files = [ name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1560,7 +1514,6 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1575,7 +1528,6 @@ cli = ["click (>=5.0)"] name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -1587,7 +1539,6 @@ files = [ name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1637,7 +1588,6 @@ files = [ name = "regex" version = "2023.5.5" description = "Alternative regular expression module, to replace re." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1735,7 +1685,6 @@ files = [ name = "requests" version = "2.29.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1757,7 +1706,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1769,7 +1717,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1781,7 +1728,6 @@ files = [ name = "sqlalchemy" version = "2.0.13" description = "Database Abstraction Library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1829,7 +1775,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""} typing-extensions = ">=4.2.0" [package.extras] @@ -1859,7 +1805,6 @@ sqlcipher = ["sqlcipher3-binary"] name = "starlette" version = "0.26.1" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1878,7 +1823,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "tenacity" version = "8.2.2" description = "Retry code until it succeeds" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1893,7 +1837,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] name = "tiktoken" version = "0.4.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1939,7 +1882,6 @@ blobfile = ["blobfile (>=2)"] name = "tokenizers" version = "0.13.3" description = "Fast and Customizable Tokenizers" -category = "main" optional = false python-versions = "*" files = [ @@ -1994,7 +1936,6 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] name = "tqdm" version = "4.65.0" description = "Fast, Extensible Progress Meter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2015,7 +1956,6 @@ telegram = ["requests"] name = "typer" version = "0.7.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2036,7 +1976,6 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2048,7 +1987,6 @@ files = [ name = "typing-inspect" version = "0.8.0" description = "Runtime inspection utilities for typing module." -category = "main" optional = false python-versions = "*" files = [ @@ -2064,7 +2002,6 @@ typing-extensions = ">=3.7.4" name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -2076,7 +2013,6 @@ files = [ name = "urllib3" version = "1.26.15" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -2093,7 +2029,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "uvicorn" version = "0.21.1" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2112,7 +2047,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "websockets" version = "11.0.2" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2192,7 +2126,6 @@ files = [ name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -2277,7 +2210,6 @@ files = [ name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2365,7 +2297,6 @@ multidict = ">=4.0" name = "zipp" version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2380,4 +2311,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "83880c8de27a4094fa8e695bd8db5d4053fc2d61ffc028f09cfa097f315c35f2" +content-hash = "53ad0b6fa45f00fe53fbd2a90845ae1647e0be22b0b52dfe55996f2c9a6faa38" diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 64d5e90c..ad5657b1 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -29,6 +29,7 @@ chevron = "^0.14.0" psutil = "^5.9.5" meilisearch = "^0.28.1" pygithub = "^1.59.0" +func-timeout = "^4.3.5" [tool.poetry.scripts] typegen = "src.continuedev.models.generate_json_schema:main" diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index fa203c28..c0957395 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -61,12 +61,12 @@ class GUIProtocolServer(AbstractGUIProtocolServer): "data": data }) - async def _receive_json(self, message_type: str, timeout: int = 5) -> Any: + async def _receive_json(self, message_type: str, timeout: int = 20) -> Any: try: return await asyncio.wait_for(self.sub_queue.get(message_type), timeout=timeout) except asyncio.TimeoutError: raise Exception( - "GUI Protocol _receive_json timed out after 5 seconds") + "GUI Protocol _receive_json timed out after 20 seconds") async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: await self._send_json(message_type, data) diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index d6a28c92..3401cbac 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -139,6 +139,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer): continue message_type = message["messageType"] data = message["data"] + print("Received message while initializing", message_type) if message_type == "workspaceDirectory": self.workspace_directory = data["workspaceDirectory"] elif message_type == "uniqueId": @@ -153,17 +154,18 @@ class IdeProtocolServer(AbstractIdeProtocolServer): async def _send_json(self, message_type: str, data: Any): if self.websocket.application_state == WebSocketState.DISCONNECTED: return + print("Sending IDE message: ", message_type) await self.websocket.send_json({ "messageType": message_type, "data": data }) - async def _receive_json(self, message_type: str, timeout: int = 5) -> Any: + async def _receive_json(self, message_type: str, timeout: int = 20) -> Any: try: return await asyncio.wait_for(self.sub_queue.get(message_type), timeout=timeout) except asyncio.TimeoutError: raise Exception( - "IDE Protocol _receive_json timed out after 5 seconds") + "IDE Protocol _receive_json timed out after 20 seconds", message_type) async def _send_and_receive_json(self, data: Any, resp_model: Type[T], message_type: str) -> T: await self._send_json(message_type, data) diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index a76bced6..1776490c 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -8,12 +8,11 @@ import "./index.css"; import posthog from "posthog-js"; import { PostHogProvider } from "posthog-js/react"; +console.log("Starting React"); + posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", { api_host: "https://app.posthog.com", - session_recording: { - // WARNING: Only enable this if you understand the security implications - recordCrossOriginIframes: true, - } as any, + disable_session_recording: true, }); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index a1d88a31..356d0256 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -17,7 +17,7 @@ export let ideProtocolClient: IdeProtocolClient; export async function activateExtension(context: vscode.ExtensionContext) { extensionContext = context; - + console.log("Using Continue version: ", getExtensionVersion()); // Before anything else, check whether this is an out-of-date version of the extension // Do so by grabbing the package.json off of the GitHub respository for now. fetch(PACKAGE_JSON_RAW_GITHUB_URL) @@ -67,6 +67,7 @@ export async function activateExtension(context: vscode.ExtensionContext) { }, 2000); }); + console.log("Continue server started"); // Initialize IDE Protocol Client const serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 802afc1d..d92a829d 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -399,8 +399,9 @@ class IdeProtocolClient { } }, 1000); }); + console.log("Getting session ID"); const resp = await this.messenger?.sendAndReceive("getSessionId", {}); - // console.log("New Continue session with ID: ", sessionId); + console.log("New Continue session with ID: ", resp.sessionId); this._sessionId = resp.sessionId; return resp.sessionId; } diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index 6da79cdc..6dcb588a 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -166,11 +166,13 @@ export function setupDebugPanel( switch (data.type) { case "onLoad": { let sessionId: string; + console.log("Running onLoad"); if (typeof sessionIdPromise === "string") { sessionId = sessionIdPromise; } else { sessionId = await sessionIdPromise; } + console.log("Done with onLoad: ", sessionId); panel.webview.postMessage({ type: "onLoad", vscMachineId: vscode.env.machineId, diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index 3044898e..e28040f5 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -39,6 +39,7 @@ export class WebsocketMessenger extends Messenger { // var WebSocket = require("ws"); // } + console.log("Creating websocket at: ", this.serverUrl); const newWebsocket = new WebSocket(this.serverUrl); for (const listener of this.onOpenListeners) { this.onOpen(listener); @@ -105,12 +106,14 @@ export class WebsocketMessenger extends Messenger { send(messageType: string, data: object) { const payload = JSON.stringify({ messageType, data }); if (this.websocket.readyState === this.websocket.OPEN) { + console.log("websocket is open, sending message: ", messageType); this.websocket.send(payload); } else { - if (this.websocket.readyState !== this.websocket.CONNECTING) { - this.websocket = this._newWebsocket(); - } + console.log("websocket is not open, creating new websocket", messageType); + this.websocket = this._newWebsocket(); + this.websocket.addEventListener("open", () => { + console.log("websocket is open, resending message: ", messageType); this.websocket.send(payload); }); } -- cgit v1.2.3-70-g09d2 From 932925ccc90096c7869797faa00b95c8ac1eff0e Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 23:46:34 -0700 Subject: correctly ignore directories in file context prov --- continuedev/src/continuedev/plugins/context_providers/file.py | 7 +++++-- extension/src/util/messenger.ts | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'extension/src/util/messenger.ts') diff --git a/continuedev/src/continuedev/plugins/context_providers/file.py b/continuedev/src/continuedev/plugins/context_providers/file.py index fc0af7ba..6222ec6a 100644 --- a/continuedev/src/continuedev/plugins/context_providers/file.py +++ b/continuedev/src/continuedev/plugins/context_providers/file.py @@ -21,7 +21,7 @@ class FileContextProvider(ContextProvider): title = "file" workspace_dir: str - ignore_patterns: List[str] = list(map(lambda folder: f"**/{folder}", [ + ignore_patterns: List[str] = [ ".git", ".vscode", ".idea", @@ -35,7 +35,10 @@ class FileContextProvider(ContextProvider): "target", "out", "bin", - ])) + ".pytest_cache", + ".vscode-test", + ".continue", + ] async def provide_context_items(self) -> List[ContextItem]: filepaths = [] diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index e28040f5..be4233b2 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -106,14 +106,11 @@ export class WebsocketMessenger extends Messenger { send(messageType: string, data: object) { const payload = JSON.stringify({ messageType, data }); if (this.websocket.readyState === this.websocket.OPEN) { - console.log("websocket is open, sending message: ", messageType); this.websocket.send(payload); } else { - console.log("websocket is not open, creating new websocket", messageType); this.websocket = this._newWebsocket(); this.websocket.addEventListener("open", () => { - console.log("websocket is open, resending message: ", messageType); this.websocket.send(payload); }); } -- cgit v1.2.3-70-g09d2 From 03da423e5abdf32c5bf9755ffd2e1c7be98e6454 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 25 Jul 2023 23:51:55 -0700 Subject: messenger don't create new websocket if connecting --- extension/src/util/messenger.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'extension/src/util/messenger.ts') diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index be4233b2..dc59a593 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -108,7 +108,9 @@ export class WebsocketMessenger extends Messenger { if (this.websocket.readyState === this.websocket.OPEN) { this.websocket.send(payload); } else { - this.websocket = this._newWebsocket(); + if (this.websocket.readyState !== this.websocket.CONNECTING) { + this.websocket = this._newWebsocket(); + } this.websocket.addEventListener("open", () => { this.websocket.send(payload); -- cgit v1.2.3-70-g09d2