summaryrefslogtreecommitdiff
path: root/continuedev/src/continuedev/server
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-07-22 22:37:13 -0700
committerNate Sesti <sestinj@gmail.com>2023-07-22 22:37:13 -0700
commit4d7e72970f770eb49627589fb142c93dfb6fd73b (patch)
tree7c85fb17a9e10ac8e387a001f021aa45c8c46582 /continuedev/src/continuedev/server
parent007780d6d60095d4e0b238358ec26b2ec776b73e (diff)
downloadsncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.tar.gz
sncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.tar.bz2
sncontinue-4d7e72970f770eb49627589fb142c93dfb6fd73b.zip
@ feature (very large commit)
Diffstat (limited to 'continuedev/src/continuedev/server')
-rw-r--r--continuedev/src/continuedev/server/gui.py37
-rw-r--r--continuedev/src/continuedev/server/gui_protocol.py10
-rw-r--r--continuedev/src/continuedev/server/main.py28
-rw-r--r--continuedev/src/continuedev/server/meilisearch_server.py56
-rw-r--r--continuedev/src/continuedev/server/session_manager.py6
5 files changed, 102 insertions, 35 deletions
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py
index ae57c0b6..36b2f3fa 100644
--- a/continuedev/src/continuedev/server/gui.py
+++ b/continuedev/src/continuedev/server/gui.py
@@ -91,25 +91,19 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
self.on_clear_history()
elif message_type == "delete_at_index":
self.on_delete_at_index(data["index"])
- elif message_type == "delete_context_at_indices":
- self.on_delete_context_at_indices(data["indices"])
+ elif message_type == "delete_context_with_ids":
+ self.on_delete_context_with_ids(data["ids"])
elif message_type == "toggle_adding_highlighted_code":
self.on_toggle_adding_highlighted_code()
elif message_type == "set_editing_at_indices":
self.on_set_editing_at_indices(data["indices"])
- elif message_type == "set_pinned_at_indices":
- self.on_set_pinned_at_indices(data["indices"])
elif message_type == "show_logs_at_index":
self.on_show_logs_at_index(data["index"])
+ elif message_type == "select_context_item":
+ self.select_context_item(data["id"], data["query"])
except Exception as e:
print(e)
- async def send_state_update(self):
- state = self.session.autopilot.get_full_state().dict()
- await self._send_json("state_update", {
- "state": state
- })
-
def on_main_input(self, input: str):
# Do something with user input
create_async_task(self.session.autopilot.accept_user_input(
@@ -144,10 +138,10 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
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]):
+ def on_delete_context_with_ids(self, ids: List[str]):
create_async_task(
- self.session.autopilot.delete_context_at_indices(
- indices), self.session.autopilot.continue_sdk.ide.unique_id
+ self.session.autopilot.delete_context_with_ids(
+ ids), self.session.autopilot.continue_sdk.ide.unique_id
)
def on_toggle_adding_highlighted_code(self):
@@ -162,18 +156,17 @@ class GUIProtocolServer(AbstractGUIProtocolServer):
indices), self.session.autopilot.continue_sdk.ide.unique_id
)
- def on_set_pinned_at_indices(self, indices: List[int]):
- create_async_task(
- self.session.autopilot.set_pinned_at_indices(
- indices), self.session.autopilot.continue_sdk.ide.unique_id
- )
-
def on_show_logs_at_index(self, index: int):
name = f"continue_logs.txt"
logs = "\n\n############################################\n\n".join(
["This is a log of the exact prompt/completion pairs sent/received from the LLM during this step"] + self.session.autopilot.continue_sdk.history.timeline[index].logs)
create_async_task(
- self.session.autopilot.ide.showVirtualFile(name, logs))
+ self.session.autopilot.ide.showVirtualFile(name, logs), self.session.autopilot.continue_sdk.ide.unique_id)
+
+ def select_context_item(self, id: str, query: str):
+ """Called when user selects an item from the dropdown"""
+ create_async_task(
+ self.session.autopilot.select_context_item(id, query), self.session.autopilot.continue_sdk.ide.unique_id)
@router.websocket("/ws")
@@ -188,7 +181,7 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we
protocol.websocket = websocket
# Update any history that may have happened before connection
- await protocol.send_state_update()
+ await protocol.session.autopilot.update_subscribers()
while AppStatus.should_exit is False:
message = await websocket.receive_text()
@@ -214,5 +207,5 @@ async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(we
if websocket.client_state != WebSocketState.DISCONNECTED:
await websocket.close()
- session_manager.persist_session(session.session_id)
+ await session_manager.persist_session(session.session_id)
session_manager.remove_session(session.session_id)
diff --git a/continuedev/src/continuedev/server/gui_protocol.py b/continuedev/src/continuedev/server/gui_protocol.py
index 9766fcd0..fb230216 100644
--- a/continuedev/src/continuedev/server/gui_protocol.py
+++ b/continuedev/src/continuedev/server/gui_protocol.py
@@ -1,6 +1,8 @@
from typing import Any, Dict, List
from abc import ABC, abstractmethod
+from ..core.context import ContextItem
+
class AbstractGUIProtocolServer(ABC):
@abstractmethod
@@ -24,10 +26,6 @@ class AbstractGUIProtocolServer(ABC):
"""Called when the user inputs a step"""
@abstractmethod
- async def send_state_update(self, state: dict):
- """Send a state update to the client"""
-
- @abstractmethod
def on_retry_at_index(self, index: int):
"""Called when the user requests a retry at a previous index"""
@@ -42,3 +40,7 @@ class AbstractGUIProtocolServer(ABC):
@abstractmethod
def on_delete_at_index(self, index: int):
"""Called when the user requests to delete a step at a given index"""
+
+ @abstractmethod
+ def select_context_item(self, id: str, query: str):
+ """Called when user selects an item from the dropdown"""
diff --git a/continuedev/src/continuedev/server/main.py b/continuedev/src/continuedev/server/main.py
index 42dc0cc1..7ee64041 100644
--- a/continuedev/src/continuedev/server/main.py
+++ b/continuedev/src/continuedev/server/main.py
@@ -1,15 +1,20 @@
+import asyncio
+import subprocess
import time
+import meilisearch
import psutil
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
-from .ide import router as ide_router
-from .gui import router as gui_router
-from .session_manager import session_manager
import atexit
import uvicorn
import argparse
+from .ide import router as ide_router
+from .gui import router as gui_router
+from .session_manager import session_manager
+from .meilisearch_server import start_meilisearch
+
app = FastAPI()
app.include_router(ide_router)
@@ -41,15 +46,20 @@ args = parser.parse_args()
# log_file = open('output.log', 'a')
# sys.stdout = log_file
-
def run_server():
uvicorn.run(app, host="0.0.0.0", port=args.port)
-def cleanup():
+async def cleanup_coroutine():
print("Cleaning up sessions")
for session_id in session_manager.sessions:
- session_manager.persist_session(session_id)
+ await session_manager.persist_session(session_id)
+
+
+def cleanup():
+ loop = asyncio.new_event_loop()
+ loop.run_until_complete(cleanup_coroutine())
+ loop.close()
def cpu_usage_report():
@@ -77,6 +87,12 @@ if __name__ == "__main__":
# cpu_thread = threading.Thread(target=cpu_usage_loop)
# cpu_thread.start()
+ try:
+ start_meilisearch()
+ except Exception as e:
+ print("Failed to start MeiliSearch")
+ print(e)
+
run_server()
except Exception as e:
cleanup()
diff --git a/continuedev/src/continuedev/server/meilisearch_server.py b/continuedev/src/continuedev/server/meilisearch_server.py
new file mode 100644
index 00000000..419f081f
--- /dev/null
+++ b/continuedev/src/continuedev/server/meilisearch_server.py
@@ -0,0 +1,56 @@
+import os
+import subprocess
+
+import meilisearch
+from ..libs.util.paths import getServerFolderPath
+
+
+def check_meilisearch_installed() -> bool:
+ """
+ Checks if MeiliSearch is installed.
+ """
+
+ serverPath = getServerFolderPath()
+ meilisearchPath = os.path.join(serverPath, "meilisearch")
+
+ return os.path.exists(meilisearchPath)
+
+
+def check_meilisearch_running() -> bool:
+ """
+ Checks if MeiliSearch is running.
+ """
+
+ try:
+ client = meilisearch.Client('http://localhost:7700')
+ resp = client.health()
+ if resp["status"] != "available":
+ return False
+ return True
+ except Exception:
+ return False
+
+
+def start_meilisearch():
+ """
+ Starts the MeiliSearch server, wait for it.
+ """
+
+ # Doesn't work on windows for now
+ if not os.name == "posix":
+ return
+
+ serverPath = getServerFolderPath()
+
+ # Check if MeiliSearch is installed
+ if not check_meilisearch_installed():
+ # Download MeiliSearch
+ print("Downloading MeiliSearch...")
+ subprocess.run(
+ f"curl -L https://install.meilisearch.com | sh", shell=True, check=True, cwd=serverPath)
+
+ # Check if MeiliSearch is running
+ if not check_meilisearch_running():
+ print("Starting MeiliSearch...")
+ subprocess.Popen(["./meilisearch"], cwd=serverPath, stdout=subprocess.DEVNULL,
+ stderr=subprocess.STDOUT, close_fds=True, start_new_session=True)
diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py
index 90172a4e..96daf92c 100644
--- a/continuedev/src/continuedev/server/session_manager.py
+++ b/continuedev/src/continuedev/server/session_manager.py
@@ -74,7 +74,7 @@ class SessionManager:
async def on_update(state: FullState):
await session_manager.send_ws_data(session_id, "state_update", {
- "state": autopilot.get_full_state().dict()
+ "state": state.dict()
})
autopilot.on_update(on_update)
@@ -84,9 +84,9 @@ class SessionManager:
def remove_session(self, session_id: str):
del self.sessions[session_id]
- def persist_session(self, session_id: str):
+ async 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()
+ full_state = await 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: