summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-07-02 20:15:36 -0700
committerNate Sesti <sestinj@gmail.com>2023-07-02 20:15:36 -0700
commit0a812994703791023125177fe4820202374e45b0 (patch)
tree7559fe0d9500d5156fa99ee5614ac0fe6d4e88f3
parenta606c13ca75f0c9177b3d04f20dcf7211d81f083 (diff)
parent0ffd2648d679916872c681036a68741a83d80c0e (diff)
downloadsncontinue-0a812994703791023125177fe4820202374e45b0.tar.gz
sncontinue-0a812994703791023125177fe4820202374e45b0.tar.bz2
sncontinue-0a812994703791023125177fe4820202374e45b0.zip
Merge branch 'main' into explicit-context
-rw-r--r--continuedev/pyproject.toml2
-rw-r--r--continuedev/src/continuedev/core/sdk.py2
-rw-r--r--continuedev/src/continuedev/libs/llm/proxy_server.py11
-rw-r--r--continuedev/src/continuedev/libs/util/dedent.py8
-rw-r--r--continuedev/src/continuedev/server/ide.py9
-rw-r--r--continuedev/src/continuedev/server/ide_protocol.py2
-rw-r--r--continuedev/src/continuedev/steps/chat.py26
-rw-r--r--continuedev/src/continuedev/steps/core/core.py43
-rw-r--r--continuedev/src/continuedev/steps/main.py7
-rw-r--r--extension/package-lock.json165
-rw-r--r--extension/package.json7
-rw-r--r--extension/react-app/src/components/TextDialog.tsx3
-rw-r--r--extension/react-app/src/tabs/gui.tsx22
-rw-r--r--extension/server_version.txt1
-rw-r--r--extension/src/activation/environmentSetup.ts87
-rw-r--r--extension/src/continueIdeClient.ts4
-rw-r--r--extension/src/suggestions.ts23
-rw-r--r--extension/src/telemetry.ts4
18 files changed, 327 insertions, 99 deletions
diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml
index e33627e7..6727e29a 100644
--- a/continuedev/pyproject.toml
+++ b/continuedev/pyproject.toml
@@ -6,7 +6,7 @@ authors = ["Nate Sesti <sestinj@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
-python = "^3.9"
+python = "^3.8"
diff-match-patch = "^20230430"
fastapi = "^0.95.1"
typer = "^0.7.0"
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index 632f8683..50a14bed 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -198,7 +198,7 @@ class ContinueSDK(AbstractContinueSDK):
# Don't insert after latest user message or function call
i = -1
- if history_context[i].role == "user" or history_context[i].role == "function":
+ if len(history_context) > 0 and (history_context[i].role == "user" or history_context[i].role == "function"):
i -= 1
history_context.insert(i, msg)
diff --git a/continuedev/src/continuedev/libs/llm/proxy_server.py b/continuedev/src/continuedev/libs/llm/proxy_server.py
index bd831ad9..69c96ee8 100644
--- a/continuedev/src/continuedev/libs/llm/proxy_server.py
+++ b/continuedev/src/continuedev/libs/llm/proxy_server.py
@@ -5,6 +5,11 @@ import aiohttp
from ...core.main import ChatMessage
from ..llm import LLM
from ..util.count_tokens import DEFAULT_ARGS, DEFAULT_MAX_TOKENS, compile_chat_messages, CHAT_MODELS, count_tokens
+import certifi
+import ssl
+
+ca_bundle_path = certifi.where()
+ssl_context = ssl.create_default_context(cafile=ca_bundle_path)
# SERVER_URL = "http://127.0.0.1:8080"
SERVER_URL = "https://proxy-server-l6vsfbzhba-uw.a.run.app"
@@ -31,7 +36,7 @@ class ProxyServer(LLM):
async def complete(self, prompt: str, with_history: List[ChatMessage] = [], **kwargs) -> Coroutine[Any, Any, str]:
args = self.default_args | kwargs
- async with aiohttp.ClientSession() as session:
+ async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl_context=ssl_context)) as session:
async with session.post(f"{SERVER_URL}/complete", json={
"messages": compile_chat_messages(args["model"], with_history, prompt, functions=None),
"unique_id": self.unique_id,
@@ -47,7 +52,7 @@ class ProxyServer(LLM):
messages = compile_chat_messages(
self.default_model, messages, None, functions=args.get("functions", None))
- async with aiohttp.ClientSession() as session:
+ async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl_context=ssl_context)) as session:
async with session.post(f"{SERVER_URL}/stream_chat", json={
"messages": messages,
"unique_id": self.unique_id,
@@ -71,7 +76,7 @@ class ProxyServer(LLM):
messages = compile_chat_messages(
self.default_model, with_history, prompt, functions=args.get("functions", None))
- async with aiohttp.ClientSession() as session:
+ async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl_context=ssl_context)) as session:
async with session.post(f"{SERVER_URL}/stream_complete", json={
"messages": messages,
"unique_id": self.unique_id,
diff --git a/continuedev/src/continuedev/libs/util/dedent.py b/continuedev/src/continuedev/libs/util/dedent.py
index 74edd173..87876d4b 100644
--- a/continuedev/src/continuedev/libs/util/dedent.py
+++ b/continuedev/src/continuedev/libs/util/dedent.py
@@ -3,11 +3,19 @@ from typing import Tuple
def dedent_and_get_common_whitespace(s: str) -> Tuple[str, str]:
lines = s.splitlines()
+ if len(lines) == 0:
+ return "", ""
# Longest common whitespace prefix
lcp = lines[0].split(lines[0].strip())[0]
+ # Iterate through the lines
for i in range(1, len(lines)):
+ # Empty lines are wildcards
+ if lines[i].strip() == "":
+ continue
+ # Iterate through the leading whitespace characters of the current line
for j in range(0, len(lcp)):
+ # If it doesn't have the same whitespace as lcp, then update lcp
if j >= len(lines[i]) or lcp[j] != lines[i][j]:
lcp = lcp[:j]
if lcp == "":
diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py
index f3deecdb..e2685493 100644
--- a/continuedev/src/continuedev/server/ide.py
+++ b/continuedev/src/continuedev/server/ide.py
@@ -7,6 +7,7 @@ import uuid
from fastapi import WebSocket, Body, APIRouter
from uvicorn.main import Server
+from ..libs.util.telemetry import capture_event
from ..libs.util.queue import AsyncSubscriptionQueue
from ..models.filesystem import FileSystem, RangeInFile, EditDiff, RangeInFileWithContents, RealFileSystem
from ..models.filesystem_edit import AddDirectory, AddFile, DeleteDirectory, DeleteFile, FileSystemEdit, FileEdit, FileEditWithFullContents, RenameDirectory, RenameFile, SequentialFileSystemEdit
@@ -145,6 +146,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
elif message_type == "commandOutput":
output = data["output"]
self.onCommandOutput(output)
+ elif message_type == "acceptRejectSuggestion":
+ self.onAcceptRejectSuggestion(data["accepted"])
elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand", "uniqueId"]:
self.sub_queue.post(message_type, data)
else:
@@ -205,8 +208,10 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
# This is where you might have triggers: plugins can subscribe to certian events
# like file changes, tracebacks, etc...
- def onAcceptRejectSuggestion(self, suggestionId: str, accepted: bool):
- pass
+ def onAcceptRejectSuggestion(self, accepted: bool):
+ capture_event(self.unique_id, "accept_reject_suggestion", {
+ "accepted": accepted
+ })
def onFileSystemUpdate(self, update: FileSystemEdit):
# Access to Autopilot (so SessionManager)
diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py
index 17a09c3d..de2eea27 100644
--- a/continuedev/src/continuedev/server/ide_protocol.py
+++ b/continuedev/src/continuedev/server/ide_protocol.py
@@ -36,7 +36,7 @@ class AbstractIdeProtocolServer(ABC):
"""Show suggestions to the user and wait for a response"""
@abstractmethod
- def onAcceptRejectSuggestion(self, suggestionId: str, accepted: bool):
+ def onAcceptRejectSuggestion(self, accepted: bool):
"""Called when the user accepts or rejects a suggestion"""
@abstractmethod
diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py
index b10ec3d7..9d556655 100644
--- a/continuedev/src/continuedev/steps/chat.py
+++ b/continuedev/src/continuedev/steps/chat.py
@@ -21,6 +21,7 @@ openai.api_key = OPENAI_API_KEY
class SimpleChatStep(Step):
user_input: str
name: str = "Chat"
+ manage_own_chat_context: bool = True
async def run(self, sdk: ContinueSDK):
self.description = f"`{self.user_input}`\n\n"
@@ -29,16 +30,35 @@ class SimpleChatStep(Step):
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()):
+ messages = await sdk.get_chat_context()
+ messages.append(ChatMessage(
+ role="user",
+ content=self.user_input,
+ summary=self.user_input
+ ))
+
+ completion = ""
+ async for chunk in sdk.models.gpt4.stream_chat(messages):
if sdk.current_step_was_deleted():
return
- self.description += chunk
- await sdk.update_ui()
+ if "content" in chunk:
+ self.description += chunk["content"]
+ completion += chunk["content"]
+ await sdk.update_ui()
self.name = (await sdk.models.gpt35.complete(
f"Write a short title for the following chat message: {self.description}")).strip()
+ if self.name.startswith('"') and self.name.endswith('"'):
+ self.name = self.name[1:-1]
+
+ self.chat_context.append(ChatMessage(
+ role="assistant",
+ content=completion,
+ summary=self.name
+ ))
+
class AddFileStep(Step):
name: str = "Add File"
diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py
index 729f5e66..4eb2445c 100644
--- a/continuedev/src/continuedev/steps/core/core.py
+++ b/continuedev/src/continuedev/steps/core/core.py
@@ -154,25 +154,32 @@ class DefaultModelEditCodeStep(Step):
_prompt_and_completion: str = ""
- async def describe(self, models: Models) -> Coroutine[str, None, None]:
- 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 (no quotes): '{self.user_input}'. This is the title:")
+ def _cleanup_output(self, output: str) -> str:
+ output = output.replace('\\"', '"')
+ output = output.replace("\\'", "'")
+ output = output.replace("\\n", "\n")
+ output = output.replace("\\t", "\t")
+ output = output.replace("\\\\", "\\")
+ if output.startswith('"') and output.endswith('"'):
+ output = output[1:-1]
- # Remove quotes from title and description if they are wrapped
- if description.startswith('"') and description.endswith('"'):
- description = description[1:-1]
+ return output
- if self.name.startswith('"') and self.name.endswith('"'):
- self.name = self.name[1:-1]
+ async def describe(self, models: Models) -> Coroutine[str, None, None]:
+ description = await models.gpt3516k.complete(dedent(f"""\
+ {self._prompt_and_completion}
+
+ Please 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:"""))
+ name = await models.gpt3516k.complete(f"Write a very short title to describe this requested change (no quotes): '{self.user_input}'. This is the title:")
+ self.name = self._cleanup_output(name)
- return f"`{self.user_input}`\n\n" + description
+ return f"`{self.user_input}`\n\n{self._cleanup_output(description)}"
async def get_prompt_parts(self, rif: RangeInFileWithContents, sdk: ContinueSDK, full_file_contents: str):
# We don't know here all of the functions being passed in.
# We care because if this prompt itself goes over the limit, then the entire message will have to be cut from the completion.
# Overflow won't happen, but prune_chat_messages in count_tokens.py will cut out this whole thing, instead of us cutting out only as many lines as we need.
- model_to_use = sdk.models.default
+ model_to_use = sdk.models.gpt4
BUFFER_FOR_FUNCTIONS = 400
total_tokens = model_to_use.count_tokens(
@@ -360,7 +367,7 @@ class DefaultModelEditCodeStep(Step):
# Insert the suggestion
replacement = "\n".join(current_block_lines)
- start_line = current_block_start + 1
+ start_line = current_block_start
end_line = current_block_start + index_of_last_line_in_block
await sdk.ide.showSuggestion(FileEdit(
filepath=rif.filepath,
@@ -368,10 +375,9 @@ class DefaultModelEditCodeStep(Step):
start_line, 0, end_line, 0),
replacement=replacement
))
- if replacement == "":
- current_line_in_file += 1
# Reset current block / update variables
+ current_line_in_file += 1
offset_from_blocks += len(current_block_lines)
original_lines_below_previous_blocks = original_lines_below_previous_blocks[
index_of_last_line_in_block + 1:]
@@ -493,7 +499,7 @@ class DefaultModelEditCodeStep(Step):
await sdk.ide.showSuggestion(FileEdit(
filepath=rif.filepath,
range=Range.from_shorthand(
- current_block_start + 1, 0, current_block_start + len(original_lines_below_previous_blocks), 0),
+ current_block_start, 0, current_block_start + len(original_lines_below_previous_blocks), 0),
replacement="\n".join(current_block_lines)
))
@@ -585,10 +591,17 @@ class UserInputStep(Step):
name: str = "User Input"
hide: bool = True
+ manage_own_chat_context: bool = True
+
async def describe(self, models: Models) -> Coroutine[str, None, None]:
return self.user_input
async def run(self, sdk: ContinueSDK) -> Coroutine[UserInputObservation, None, None]:
+ self.chat_context.append(ChatMessage(
+ role="user",
+ content=self.user_input,
+ summary=self.user_input
+ ))
return UserInputObservation(user_input=self.user_input)
diff --git a/continuedev/src/continuedev/steps/main.py b/continuedev/src/continuedev/steps/main.py
index def1af4e..3cf78c40 100644
--- a/continuedev/src/continuedev/steps/main.py
+++ b/continuedev/src/continuedev/steps/main.py
@@ -266,6 +266,13 @@ class EditHighlightedCodeStep(Step):
range_in_files = [RangeInFile.from_entire_file(
filepath, content) for filepath, content in contents.items()]
+ # If still no highlighted code, create a new file and edit there
+ if len(range_in_files) == 0:
+ # Create a new file
+ new_file_path = "new_file.txt"
+ await sdk.add_file(new_file_path)
+ range_in_files = [RangeInFile.from_entire_file(new_file_path, "")]
+
await sdk.run_step(DefaultModelEditCodeStep(user_input=self.user_input, range_in_files=range_in_files))
diff --git a/extension/package-lock.json b/extension/package-lock.json
index a3b528ac..0a1ba1b6 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,17 +1,18 @@
{
"name": "continue",
- "version": "0.0.99",
+ "version": "0.0.104",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.99",
+ "version": "0.0.104",
"license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
"@reduxjs/toolkit": "^1.9.3",
"@segment/analytics-node": "^0.0.1-beta.16",
+ "@sentry/node": "^7.57.0",
"@styled-icons/heroicons-outline": "^10.47.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"axios": "^1.2.5",
@@ -35,7 +36,7 @@
"@types/node-fetch": "^2.6.2",
"@types/react-dom": "^18.2.4",
"@types/styled-components": "^5.1.26",
- "@types/vscode": "^1.74.0",
+ "@types/vscode": "1.60",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
@@ -49,7 +50,7 @@
"vsce": "^2.15.0"
},
"engines": {
- "vscode": "^1.74.0"
+ "vscode": "^1.60.0"
}
},
"node_modules/@alloc/quick-lru": {
@@ -1331,6 +1332,71 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
+ "node_modules/@sentry-internal/tracing": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz",
+ "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==",
+ "dependencies": {
+ "@sentry/core": "7.57.0",
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@sentry/core": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz",
+ "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==",
+ "dependencies": {
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@sentry/node": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.57.0.tgz",
+ "integrity": "sha512-63mjyUVM6sfJFVQ5TGVRVGUsoEfESl5ABzIW1W0s9gUiQPaG8SOdaQJglb2VNrkMYxnRHgD8Q9LUh/qcmUyPGw==",
+ "dependencies": {
+ "@sentry-internal/tracing": "7.57.0",
+ "@sentry/core": "7.57.0",
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "cookie": "^0.4.1",
+ "https-proxy-agent": "^5.0.0",
+ "lru_map": "^0.3.3",
+ "tslib": "^2.4.1 || ^1.9.3"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@sentry/types": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz",
+ "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@sentry/utils": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz",
+ "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==",
+ "dependencies": {
+ "@sentry/types": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
@@ -1774,9 +1840,9 @@
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@types/vscode": {
- "version": "1.74.0",
- "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.74.0.tgz",
- "integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.60.0.tgz",
+ "integrity": "sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==",
"dev": true
},
"node_modules/@types/ws": {
@@ -2940,6 +3006,14 @@
"node": "> 0.10"
}
},
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -5183,6 +5257,11 @@
"node": ">=8"
}
},
+ "node_modules/lru_map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
+ },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -8183,8 +8262,7 @@
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -9808,6 +9886,56 @@
}
}
},
+ "@sentry-internal/tracing": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.57.0.tgz",
+ "integrity": "sha512-tpViyDd8AhQGYYhI94xi2aaDopXOPfL2Apwrtb3qirWkomIQ2K86W1mPmkce+B0cFOnW2Dxv/ZTFKz6ghjK75A==",
+ "requires": {
+ "@sentry/core": "7.57.0",
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ }
+ },
+ "@sentry/core": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.57.0.tgz",
+ "integrity": "sha512-l014NudPH0vQlzybtXajPxYFfs9w762NoarjObC3gu76D1jzBBFzhdRelkGpDbSLNTIsKhEDDRpgAjBWJ9icfw==",
+ "requires": {
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ }
+ },
+ "@sentry/node": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.57.0.tgz",
+ "integrity": "sha512-63mjyUVM6sfJFVQ5TGVRVGUsoEfESl5ABzIW1W0s9gUiQPaG8SOdaQJglb2VNrkMYxnRHgD8Q9LUh/qcmUyPGw==",
+ "requires": {
+ "@sentry-internal/tracing": "7.57.0",
+ "@sentry/core": "7.57.0",
+ "@sentry/types": "7.57.0",
+ "@sentry/utils": "7.57.0",
+ "cookie": "^0.4.1",
+ "https-proxy-agent": "^5.0.0",
+ "lru_map": "^0.3.3",
+ "tslib": "^2.4.1 || ^1.9.3"
+ }
+ },
+ "@sentry/types": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.57.0.tgz",
+ "integrity": "sha512-D7ifoUfxuVCUyktIr5Gc+jXUbtcUMmfHdTtTbf1XCZHua5mJceK9wtl3YCg3eq/HK2Ppd52BKnTzEcS5ZKQM+w=="
+ },
+ "@sentry/utils": {
+ "version": "7.57.0",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.57.0.tgz",
+ "integrity": "sha512-YXrkMCiNklqkXctn4mKYkrzNCf/dfVcRUQrkXjeBC+PHXbcpPyaJgInNvztR7Skl8lE3JPGPN4v5XhLxK1bUUg==",
+ "requires": {
+ "@sentry/types": "7.57.0",
+ "tslib": "^2.4.1 || ^1.9.3"
+ }
+ },
"@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
@@ -10116,9 +10244,9 @@
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"@types/vscode": {
- "version": "1.74.0",
- "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.74.0.tgz",
- "integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.60.0.tgz",
+ "integrity": "sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==",
"dev": true
},
"@types/ws": {
@@ -10937,6 +11065,11 @@
"easy-table": "1.1.0"
}
},
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+ },
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -12603,6 +12736,11 @@
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
+ "lru_map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="
+ },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -14662,8 +14800,7 @@
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"tsutils": {
"version": "3.21.0",
diff --git a/extension/package.json b/extension/package.json
index 93649e3a..2a375138 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -14,10 +14,10 @@
"displayName": "Continue",
"pricing": "Free",
"description": "The open-source coding autopilot",
- "version": "0.0.99",
+ "version": "0.0.104",
"publisher": "Continue",
"engines": {
- "vscode": "^1.74.0"
+ "vscode": "^1.60.0"
},
"categories": [
"Other",
@@ -223,7 +223,7 @@
"@types/node-fetch": "^2.6.2",
"@types/react-dom": "^18.2.4",
"@types/styled-components": "^5.1.26",
- "@types/vscode": "^1.74.0",
+ "@types/vscode": "1.60",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
@@ -240,6 +240,7 @@
"@electron/rebuild": "^3.2.10",
"@reduxjs/toolkit": "^1.9.3",
"@segment/analytics-node": "^0.0.1-beta.16",
+ "@sentry/node": "^7.57.0",
"@styled-icons/heroicons-outline": "^10.47.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"axios": "^1.2.5",
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index 2632e572..a564f884 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -52,6 +52,7 @@ const TextDialog = (props: {
showDialog: boolean;
onEnter: (text: string) => void;
onClose: () => void;
+ message?: string;
}) => {
const [text, setText] = useState("");
const textAreaRef = React.createRef<HTMLTextAreaElement>();
@@ -75,7 +76,7 @@ const TextDialog = (props: {
}}
>
<Dialog>
- <P>Thanks for your feedback. We'll get back to you soon!</P>
+ <P>{props.message || ""}</P>
<TextArea
rows={10}
ref={textAreaRef}
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 658aa503..e0b32a24 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -103,6 +103,7 @@ function GUI(props: GUIProps) {
} as any);
const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);
+ const [feedbackDialogMessage, setFeedbackDialogMessage] = useState("");
const topGuiDivRef = useRef<HTMLDivElement>(null);
const client = useContinueGUIProtocol();
@@ -259,6 +260,7 @@ function GUI(props: GUIProps) {
onClose={() => {
setShowFeedbackDialog(false);
}}
+ message={feedbackDialogMessage}
></TextDialog>
<TopGUIDiv
@@ -396,17 +398,24 @@ function GUI(props: GUIProps) {
</div>
<HeaderButtonWithText
onClick={() => {
- client?.changeDefaultModel(
- usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
- );
+ // client?.changeDefaultModel(
+ // usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
+ // );
+ if (!usingFastModel) {
+ // Show the dialog
+ setFeedbackDialogMessage(
+ "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please use the feedback button in the bottom right to let us know."
+ );
+ setShowFeedbackDialog(true);
+ }
setUsingFastModel((prev) => !prev);
}}
- text={usingFastModel ? "gpt-3.5-turbo" : "gpt-4"}
+ text={usingFastModel ? "local" : "gpt-4"}
>
<div
style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }}
>
- {usingFastModel ? "⚡" : "🧠"}
+ {usingFastModel ? "🔒" : "🧠"}
</div>
</HeaderButtonWithText>
<HeaderButtonWithText
@@ -428,6 +437,9 @@ function GUI(props: GUIProps) {
<HeaderButtonWithText
onClick={() => {
// Set dialog open
+ setFeedbackDialogMessage(
+ "Having trouble using Continue? Want a new feature? Let us know! This box is anonymous, but we will promptly address your feedback."
+ );
setShowFeedbackDialog(true);
}}
text="Feedback"
diff --git a/extension/server_version.txt b/extension/server_version.txt
new file mode 100644
index 00000000..da5ce208
--- /dev/null
+++ b/extension/server_version.txt
@@ -0,0 +1 @@
+0.0.101 \ No newline at end of file
diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts
index d4c81d2e..4e6be9b3 100644
--- a/extension/src/activation/environmentSetup.ts
+++ b/extension/src/activation/environmentSetup.ts
@@ -9,6 +9,7 @@ import { getContinueServerUrl } from "../bridge";
import fetch from "node-fetch";
import * as vscode from "vscode";
import fkill from "fkill";
+import { sendTelemetryEvent, TelemetryEvent } from "../telemetry";
const MAX_RETRIES = 5;
async function retryThenFail(
@@ -17,13 +18,16 @@ async function retryThenFail(
): Promise<any> {
try {
return await fn();
- } catch (e) {
+ } catch (e: any) {
if (retries > 0) {
return await retryThenFail(fn, retries - 1);
}
vscode.window.showErrorMessage(
"Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!"
);
+ sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, {
+ error: e.message,
+ });
throw e;
}
}
@@ -47,6 +51,12 @@ async function runCommand(cmd: string): Promise<[string, string | undefined]> {
stdout = "";
}
+ if (stderr) {
+ sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, {
+ error: stderr,
+ });
+ }
+
return [stdout, stderr];
}
@@ -71,9 +81,11 @@ async function getPythonPipCommands() {
}
}
+ let pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip";
+
const version = stdout.split(" ")[1];
const [major, minor] = version.split(".");
- if (parseInt(major) !== 3 || parseInt(minor) < 7) {
+ if (parseInt(major) !== 3 || parseInt(minor) < 8) {
// Need to check specific versions
const checkPython3VersionExists = async (minorVersion: number) => {
const [stdout, stderr] = await runCommand(
@@ -82,24 +94,27 @@ async function getPythonPipCommands() {
return typeof stderr === "undefined" || stderr === "";
};
- const validVersions = [7, 8, 9, 10, 11, 12];
+ const VALID_VERSIONS = [8, 9, 10, 11, 12];
let versionExists = false;
- for (const minorVersion of validVersions) {
+ for (const minorVersion of VALID_VERSIONS) {
if (await checkPython3VersionExists(minorVersion)) {
versionExists = true;
- break;
+ pythonCmd = `python3.${minorVersion}`;
+ pipCmd = `pip3.${minorVersion}`;
}
}
if (!versionExists) {
vscode.window.showErrorMessage(
- "Continue requires Python3 version 3.7 or greater. Please update your Python3 installation, reload VS Code, and try again."
+ "Continue requires Python3 version 3.8 or greater. Please update your Python3 installation, reload VS Code, and try again."
);
- throw new Error("Python3 is not installed.");
+ throw new Error("Python3.8 or greater is not installed.");
}
+ } else {
+ pythonCmd = `python${major}.${minor}`;
+ pipCmd = `pip${major}.${minor}`;
}
- const pipCmd = pythonCmd.endsWith("3") ? "pip3" : "pip";
return [pythonCmd, pipCmd];
}
@@ -283,34 +298,33 @@ export async function startContinuePythonServer() {
return;
}
- if (await checkServerRunning(serverUrl)) {
- // Kill the server if it is running an old version
- if (fs.existsSync(serverVersionPath())) {
- const serverVersion = fs.readFileSync(serverVersionPath(), "utf8");
- if (serverVersion === getExtensionVersion()) {
- return;
+ return await retryThenFail(async () => {
+ if (await checkServerRunning(serverUrl)) {
+ // Kill the server if it is running an old version
+ if (fs.existsSync(serverVersionPath())) {
+ const serverVersion = fs.readFileSync(serverVersionPath(), "utf8");
+ if (serverVersion === getExtensionVersion()) {
+ return;
+ }
}
+ console.log("Killing old server...");
+ await fkill(":65432");
}
- console.log("Killing old server...");
- await fkill(":65432");
- }
- // Do this after above check so we don't have to waste time setting up the env
- await setupPythonEnv();
+ // Do this after above check so we don't have to waste time setting up the env
+ await setupPythonEnv();
- let activateCmd = ". env/bin/activate";
- let pythonCmd = "python3";
- if (process.platform == "win32") {
- activateCmd = ".\\env\\Scripts\\activate";
- pythonCmd = "python";
- }
+ const [pythonCmd] = await getPythonPipCommands();
+ const activateCmd =
+ process.platform == "win32"
+ ? ".\\env\\Scripts\\activate"
+ : ". env/bin/activate";
- let command = `cd "${path.join(
- getExtensionUri().fsPath,
- "scripts"
- )}" && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`;
+ const command = `cd "${path.join(
+ getExtensionUri().fsPath,
+ "scripts"
+ )}" && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`;
- return await retryThenFail(async () => {
console.log("Starting Continue python server...");
return new Promise(async (resolve, reject) => {
@@ -318,22 +332,25 @@ export async function startContinuePythonServer() {
const child = spawn(command, {
shell: true,
});
- child.stdout.on("data", (data: any) => {
- console.log(`stdout: ${data}`);
- });
child.stderr.on("data", (data: any) => {
+ console.log(`stdout: ${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}`);
+ } else if (data.includes("ERROR") || data.includes("Traceback")) {
+ sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, {
+ error: data,
+ });
}
});
child.on("error", (error: any) => {
console.log(`error: ${error.message}`);
+ sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, {
+ error: error.message,
+ });
});
// Write the current version of vscode to a file called server_version.txt
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 8f45b849..999bca88 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -408,6 +408,10 @@ class IdeProtocolClient {
sendHighlightedCode(highlightedCode: (RangeInFile & { contents: string })[]) {
this.messenger?.send("highlightedCodePush", { highlightedCode });
}
+
+ sendAcceptRejectSuggestion(accepted: boolean) {
+ this.messenger?.send("acceptRejectSuggestion", { accepted });
+ }
}
export default IdeProtocolClient;
diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts
index baa49711..6e5a444f 100644
--- a/extension/src/suggestions.ts
+++ b/extension/src/suggestions.ts
@@ -5,7 +5,7 @@ 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";
+import { extensionContext, ideProtocolClient } from "./activation/activate";
export interface SuggestionRanges {
oldRange: vscode.Range;
@@ -241,19 +241,14 @@ function selectSuggestion(
suggestions = JSON.parse(rawData);
}
- if (accept === "new" || (accept === "selected" && suggestion.newSelected)) {
- suggestions.push({
- accepted: true,
- timestamp: Date.now(),
- suggestion: suggestion.newContent,
- });
- } else {
- suggestions.push({
- accepted: false,
- timestamp: Date.now(),
- suggestion: suggestion.newContent,
- });
- }
+ const accepted =
+ accept === "new" || (accept === "selected" && suggestion.newSelected);
+ suggestions.push({
+ accepted,
+ timestamp: Date.now(),
+ suggestion: suggestion.newContent,
+ });
+ ideProtocolClient.sendAcceptRejectSuggestion(accepted);
// Write the updated suggestions back to the file
fs.writeFileSync(
diff --git a/extension/src/telemetry.ts b/extension/src/telemetry.ts
index ea71a545..db5cb8ca 100644
--- a/extension/src/telemetry.ts
+++ b/extension/src/telemetry.ts
@@ -35,6 +35,8 @@ export enum TelemetryEvent {
AutoDebugThisTest = "AutoDebugThisTest",
// Command run to generate docstring
GenerateDocstring = "GenerateDocstring",
+ // Error setting up the extension
+ ExtensionSetupError = "ExtensionSetupError",
}
export function sendTelemetryEvent(
@@ -48,4 +50,4 @@ export function sendTelemetryEvent(
userId: vscode.env.machineId,
properties,
});
-} \ No newline at end of file
+}