diff options
Diffstat (limited to 'continuedev/src')
-rw-r--r-- | continuedev/src/continuedev/core/sdk.py | 2 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/proxy_server.py | 11 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/util/dedent.py | 8 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide.py | 9 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide_protocol.py | 2 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/chat.py | 26 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/core/core.py | 43 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/main.py | 7 |
8 files changed, 83 insertions, 25 deletions
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)) |