From 55ff206a9c24e67cd1f32ec86d705206530e668f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 10 Jul 2023 19:31:48 -0700 Subject: catch errors during asyncio tasks --- continuedev/src/continuedev/core/autopilot.py | 4 +- .../src/continuedev/libs/util/create_async_task.py | 23 +++++++++ continuedev/src/continuedev/server/gui.py | 54 +++++++++++++--------- continuedev/src/continuedev/server/ide.py | 20 ++++---- .../src/continuedev/server/session_manager.py | 9 ++-- .../src/continuedev/steps/search_directory.py | 5 +- extension/src/continueIdeClient.ts | 2 +- 7 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 continuedev/src/continuedev/libs/util/create_async_task.py diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 5c3baafd..615e7657 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -20,6 +20,7 @@ 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 +from ..libs.util.create_async_task import create_async_task def get_error_title(e: Exception) -> str: @@ -341,7 +342,8 @@ class Autopilot(ContinueBaseModel): # Update subscribers with new description await self.update_subscribers() - asyncio.create_task(update_description()) + create_async_task(update_description(), + self.continue_sdk.ide.unique_id) return observation diff --git a/continuedev/src/continuedev/libs/util/create_async_task.py b/continuedev/src/continuedev/libs/util/create_async_task.py new file mode 100644 index 00000000..89b84868 --- /dev/null +++ b/continuedev/src/continuedev/libs/util/create_async_task.py @@ -0,0 +1,23 @@ +from typing import Coroutine, Union +import traceback +from .telemetry import capture_event +import asyncio +import nest_asyncio +nest_asyncio.apply() + + +def create_async_task(coro: Coroutine, unique_id: Union[str, None] = None): + """asyncio.create_task and log errors by adding a callback""" + task = asyncio.create_task(coro) + + def callback(future: asyncio.Future): + try: + future.result() + except Exception as e: + print("Exception caught from async task: ", e) + capture_event(unique_id or "None", "async_task_error", { + "error_title": e.__str__() or e.__repr__(), "error_message": traceback.format(e.__traceback__) + }) + + task.add_done_callback(callback) + return task diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 8e9b1fb9..8daad73a 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -7,9 +7,8 @@ from uvicorn.main import Server from .session_manager import SessionManager, session_manager, Session from .gui_protocol import AbstractGUIProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue -import asyncio -import nest_asyncio -nest_asyncio.apply() +from ..libs.util.telemetry import capture_event +from ..libs.util.create_async_task import create_async_task router = APIRouter(prefix="/gui", tags=["gui"]) @@ -102,51 +101,60 @@ class GUIProtocolServer(AbstractGUIProtocolServer): def on_main_input(self, input: str): # Do something with user input - asyncio.create_task(self.session.autopilot.accept_user_input(input)) + create_async_task(self.session.autopilot.accept_user_input( + input), self.session.autopilot.continue_sdk.ide.unique_id) def on_reverse_to_index(self, index: int): # Reverse the history to the given index - asyncio.create_task(self.session.autopilot.reverse_to_index(index)) + create_async_task(self.session.autopilot.reverse_to_index( + index), self.session.autopilot.continue_sdk.ide.unique_id) def on_step_user_input(self, input: str, index: int): - asyncio.create_task( - self.session.autopilot.give_user_input(input, index)) + create_async_task( + self.session.autopilot.give_user_input(input, index), self.session.autopilot.continue_sdk.ide.unique_id) def on_refinement_input(self, input: str, index: int): - asyncio.create_task( - self.session.autopilot.accept_refinement_input(input, index)) + create_async_task( + self.session.autopilot.accept_refinement_input(input, index), self.session.autopilot.continue_sdk.ide.unique_id) def on_retry_at_index(self, index: int): - asyncio.create_task( - self.session.autopilot.retry_at_index(index)) + create_async_task( + self.session.autopilot.retry_at_index(index), self.session.autopilot.continue_sdk.ide.unique_id) def on_change_default_model(self, model: str): - asyncio.create_task(self.session.autopilot.change_default_model(model)) + create_async_task(self.session.autopilot.change_default_model( + model), self.session.autopilot.continue_sdk.ide.unique_id) def on_clear_history(self): - asyncio.create_task(self.session.autopilot.clear_history()) + create_async_task(self.session.autopilot.clear_history( + ), self.session.autopilot.continue_sdk.ide.unique_id) def on_delete_at_index(self, index: int): - asyncio.create_task(self.session.autopilot.delete_at_index(index)) + create_async_task(self.session.autopilot.delete_at_index( + index), self.session.autopilot.continue_sdk.ide.unique_id) def on_delete_context_at_indices(self, indices: List[int]): - asyncio.create_task( - self.session.autopilot.delete_context_at_indices(indices) + create_async_task( + self.session.autopilot.delete_context_at_indices( + indices), self.session.autopilot.continue_sdk.ide.unique_id ) def on_toggle_adding_highlighted_code(self): - asyncio.create_task( - self.session.autopilot.toggle_adding_highlighted_code() + create_async_task( + self.session.autopilot.toggle_adding_highlighted_code( + ), self.session.autopilot.continue_sdk.ide.unique_id ) def on_set_editing_at_indices(self, indices: List[int]): - asyncio.create_task( - self.session.autopilot.set_editing_at_indices(indices) + create_async_task( + self.session.autopilot.set_editing_at_indices( + indices), self.session.autopilot.continue_sdk.ide.unique_id ) def on_set_pinned_at_indices(self, indices: List[int]): - asyncio.create_task( - self.session.autopilot.set_pinned_at_indices(indices) + create_async_task( + self.session.autopilot.set_pinned_at_indices( + indices), self.session.autopilot.continue_sdk.ide.unique_id ) @@ -179,6 +187,8 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we except Exception as e: print("ERROR in gui websocket: ", e) + capture_event(session.autopilot.continue_sdk.ide.unique_id, "gui_error", { + "error_title": e.__str__() or e.__repr__(), "error_message": e.__traceback__}) raise e finally: print("Closing gui websocket") diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index e4a6266a..f84f3de2 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -15,8 +15,7 @@ from pydantic import BaseModel from .gui import SessionManager, session_manager from .ide_protocol import AbstractIdeProtocolServer import asyncio -import nest_asyncio -nest_asyncio.apply() +from ..libs.util.create_async_task import create_async_task router = APIRouter(prefix="/ide", tags=["ide"]) @@ -250,24 +249,25 @@ class IdeProtocolServer(AbstractIdeProtocolServer): def onDeleteAtIndex(self, index: int): for _, session in self.session_manager.sessions.items(): - asyncio.create_task(session.autopilot.delete_at_index(index)) + create_async_task( + session.autopilot.delete_at_index(index), self.unique_id) def onCommandOutput(self, output: str): # Send the output to ALL autopilots. # Maybe not ideal behavior for _, session in self.session_manager.sessions.items(): - asyncio.create_task( - session.autopilot.handle_command_output(output)) + create_async_task( + session.autopilot.handle_command_output(output), self.unique_id) def onHighlightedCodeUpdate(self, range_in_files: List[RangeInFileWithContents]): for _, session in self.session_manager.sessions.items(): - asyncio.create_task( - session.autopilot.handle_highlighted_code(range_in_files)) + create_async_task( + session.autopilot.handle_highlighted_code(range_in_files), self.unique_id) def onMainUserInput(self, input: str): for _, session in self.session_manager.sessions.items(): - asyncio.create_task( - session.autopilot.accept_user_input(input)) + create_async_task( + session.autopilot.accept_user_input(input), self.unique_id) # Request information. Session doesn't matter. async def getOpenFiles(self) -> List[str]: @@ -412,5 +412,7 @@ async def websocket_endpoint(websocket: WebSocket): await websocket.close() except Exception as e: print("Error in ide websocket: ", e) + capture_event(ideProtocolServer.unique_id, "gui_error", { + "error_title": e.__str__() or e.__repr__(), "error_message": e.__traceback__}) await websocket.close() raise e diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index 99a38146..873a379e 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -1,3 +1,4 @@ +from asyncio import BaseEventLoop from fastapi import WebSocket from typing import Any, Dict, List, Union from uuid import uuid4 @@ -7,9 +8,7 @@ from ..core.policy import DemoPolicy from ..core.main import FullState from ..core.autopilot import Autopilot from .ide_protocol import AbstractIdeProtocolServer -import asyncio -import nest_asyncio -nest_asyncio.apply() +from ..libs.util.create_async_task import create_async_task class Session: @@ -38,7 +37,7 @@ class DemoAutopilot(Autopilot): class SessionManager: sessions: Dict[str, Session] = {} - _event_loop: Union[asyncio.BaseEventLoop, None] = None + _event_loop: Union[BaseEventLoop, None] = None def get_session(self, session_id: str) -> Session: if session_id not in self.sessions: @@ -57,7 +56,7 @@ class SessionManager: }) autopilot.on_update(on_update) - asyncio.create_task(autopilot.run_policy()) + create_async_task(autopilot.run_policy()) return session_id def remove_session(self, session_id: str): diff --git a/continuedev/src/continuedev/steps/search_directory.py b/continuedev/src/continuedev/steps/search_directory.py index 2eecc99c..bfb97630 100644 --- a/continuedev/src/continuedev/steps/search_directory.py +++ b/continuedev/src/continuedev/steps/search_directory.py @@ -6,6 +6,7 @@ from ..models.filesystem import RangeInFile from ..models.main import Range from ..core.main import Step from ..core.sdk import ContinueSDK +from ..libs.util.create_async_task import create_async_task import os import re @@ -60,9 +61,9 @@ class EditAllMatchesStep(Step): # Search all files for a given string range_in_files = find_all_matches_in_dir(self.pattern, self.directory or await sdk.ide.getWorkspaceDirectory()) - tasks = [asyncio.create_task(sdk.edit_file( + tasks = [create_async_task(sdk.edit_file( range=range_in_file.range, filename=range_in_file.filepath, prompt=self.user_request - )) for range_in_file in range_in_files] + ), sdk.ide.unique_id) for range_in_file in range_in_files] await asyncio.gather(*tasks) diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 679d94ba..304c592b 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -300,7 +300,7 @@ class IdeProtocolClient { }); const resp = await this.messenger?.sendAndReceive("openGUI", {}); const sessionId = resp.sessionId; - console.log("New Continue session with ID: ", sessionId); + // console.log("New Continue session with ID: ", sessionId); return sessionId; } -- cgit v1.2.3-70-g09d2 From a8c91ffaccd181a0f6c28e3d218c3c7eeb0282c3 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 10 Jul 2023 20:08:04 -0700 Subject: gifs in outside media folder --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/public/edit.gif | Bin 41667733 -> 0 bytes extension/react-app/public/explain.gif | Bin 56475028 -> 0 bytes extension/react-app/public/generate.gif | Bin 32532380 -> 0 bytes extension/react-app/public/intro.gif | Bin 3976676 -> 0 bytes extension/react-app/src/components/Onboarding.tsx | 2 +- media/edit.gif | Bin 0 -> 41667733 bytes media/explain.gif | Bin 0 -> 56475028 bytes media/generate.gif | Bin 0 -> 32532380 bytes media/intro.gif | Bin 0 -> 3976676 bytes 11 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 extension/react-app/public/edit.gif delete mode 100644 extension/react-app/public/explain.gif delete mode 100644 extension/react-app/public/generate.gif delete mode 100644 extension/react-app/public/intro.gif create mode 100644 media/edit.gif create mode 100644 media/explain.gif create mode 100644 media/generate.gif create mode 100644 media/intro.gif diff --git a/extension/package-lock.json b/extension/package-lock.json index 5733c2dd..52b2e7b9 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.143", + "version": "0.0.146", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.143", + "version": "0.0.146", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 0477a450..393c5d70 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.143", + "version": "0.0.146", "publisher": "Continue", "engines": { "vscode": "^1.67.0" diff --git a/extension/react-app/public/edit.gif b/extension/react-app/public/edit.gif deleted file mode 100644 index 6780cdf7..00000000 Binary files a/extension/react-app/public/edit.gif and /dev/null differ diff --git a/extension/react-app/public/explain.gif b/extension/react-app/public/explain.gif deleted file mode 100644 index e74803dc..00000000 Binary files a/extension/react-app/public/explain.gif and /dev/null differ diff --git a/extension/react-app/public/generate.gif b/extension/react-app/public/generate.gif deleted file mode 100644 index 5c1d112b..00000000 Binary files a/extension/react-app/public/generate.gif and /dev/null differ diff --git a/extension/react-app/public/intro.gif b/extension/react-app/public/intro.gif deleted file mode 100644 index f872dc91..00000000 Binary files a/extension/react-app/public/intro.gif and /dev/null differ diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx index 0e188e7a..1cbe61ec 100644 --- a/extension/react-app/src/components/Onboarding.tsx +++ b/extension/react-app/src/components/Onboarding.tsx @@ -76,7 +76,7 @@ const Onboarding = () => {

{topMessages[counter]}

{topMessages[counter]}
diff --git a/media/edit.gif b/media/edit.gif new file mode 100644 index 00000000..6780cdf7 Binary files /dev/null and b/media/edit.gif differ diff --git a/media/explain.gif b/media/explain.gif new file mode 100644 index 00000000..e74803dc Binary files /dev/null and b/media/explain.gif differ diff --git a/media/generate.gif b/media/generate.gif new file mode 100644 index 00000000..5c1d112b Binary files /dev/null and b/media/generate.gif differ diff --git a/media/intro.gif b/media/intro.gif new file mode 100644 index 00000000..f872dc91 Binary files /dev/null and b/media/intro.gif differ -- cgit v1.2.3-70-g09d2 From 68193a76211472ff5e9265f6f7aa86e2d406fa7f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 10 Jul 2023 20:10:32 -0700 Subject: patch --- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extension/package-lock.json b/extension/package-lock.json index 52b2e7b9..71f4d974 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.146", + "version": "0.0.147", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.146", + "version": "0.0.147", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 393c5d70..c41fa549 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.146", + "version": "0.0.147", "publisher": "Continue", "engines": { "vscode": "^1.67.0" -- cgit v1.2.3-70-g09d2 From b8a19ce1b8f4a37dfbbb438ce0fac4ae4aa4358b Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 10 Jul 2023 23:07:23 -0700 Subject: fixed gif link --- extension/react-app/src/components/Onboarding.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx index 1cbe61ec..061faba5 100644 --- a/extension/react-app/src/components/Onboarding.tsx +++ b/extension/react-app/src/components/Onboarding.tsx @@ -76,7 +76,7 @@ const Onboarding = () => {

{topMessages[counter]}

{topMessages[counter]}
-- cgit v1.2.3-70-g09d2 From 20797dbdcdbcfc75d4eaa7cdfccfd384ecbdecd8 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 10 Jul 2023 23:12:21 -0700 Subject: traceback.format( -> format_tb --- continuedev/src/continuedev/libs/util/create_async_task.py | 2 +- continuedev/src/continuedev/server/gui.py | 3 ++- continuedev/src/continuedev/server/ide.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/continuedev/src/continuedev/libs/util/create_async_task.py b/continuedev/src/continuedev/libs/util/create_async_task.py index 89b84868..608d4977 100644 --- a/continuedev/src/continuedev/libs/util/create_async_task.py +++ b/continuedev/src/continuedev/libs/util/create_async_task.py @@ -16,7 +16,7 @@ def create_async_task(coro: Coroutine, unique_id: Union[str, None] = None): except Exception as e: print("Exception caught from async task: ", e) capture_event(unique_id or "None", "async_task_error", { - "error_title": e.__str__() or e.__repr__(), "error_message": traceback.format(e.__traceback__) + "error_title": e.__str__() or e.__repr__(), "error_message": traceback.format_tb(e.__traceback__) }) task.add_done_callback(callback) diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 8daad73a..ae53be00 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -2,6 +2,7 @@ import json from fastapi import Depends, Header, WebSocket, APIRouter from typing import Any, List, Type, TypeVar, Union from pydantic import BaseModel +import traceback from uvicorn.main import Server from .session_manager import SessionManager, session_manager, Session @@ -188,7 +189,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we except Exception as e: print("ERROR in gui websocket: ", e) capture_event(session.autopilot.continue_sdk.ide.unique_id, "gui_error", { - "error_title": e.__str__() or e.__repr__(), "error_message": e.__traceback__}) + "error_title": e.__str__() or e.__repr__(), "error_message": traceback.format_tb(e.__traceback__)}) raise e finally: print("Closing gui websocket") diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index f84f3de2..93996edd 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List, Type, TypeVar, Union import uuid from fastapi import WebSocket, Body, APIRouter from uvicorn.main import Server +import traceback from ..libs.util.telemetry import capture_event from ..libs.util.queue import AsyncSubscriptionQueue @@ -413,6 +414,6 @@ async def websocket_endpoint(websocket: WebSocket): except Exception as e: print("Error in ide websocket: ", e) capture_event(ideProtocolServer.unique_id, "gui_error", { - "error_title": e.__str__() or e.__repr__(), "error_message": e.__traceback__}) + "error_title": e.__str__() or e.__repr__(), "error_message": traceback.format_tb(e.__traceback__)}) await websocket.close() raise e -- cgit v1.2.3-70-g09d2