summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--continuedev/src/continuedev/core/autopilot.py4
-rw-r--r--continuedev/src/continuedev/libs/util/edit_config.py19
-rw-r--r--continuedev/src/continuedev/plugins/steps/core/core.py49
-rw-r--r--continuedev/src/continuedev/plugins/steps/setup_model.py11
-rw-r--r--continuedev/src/continuedev/server/gui.py10
-rw-r--r--extension/react-app/src/components/Layout.tsx54
-rw-r--r--extension/react-app/src/components/ModelSelect.tsx164
-rw-r--r--extension/react-app/src/components/TextDialog.tsx2
-rw-r--r--extension/react-app/src/components/UserInputContainer.tsx4
-rw-r--r--extension/react-app/src/components/dialogs/AddContextGroupDialog.tsx2
-rw-r--r--extension/react-app/src/components/dialogs/SelectContextGroupDialog.tsx2
-rw-r--r--extension/react-app/src/pages/gui.tsx4
12 files changed, 211 insertions, 114 deletions
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index 5f17b4d2..cee7a2f9 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -464,7 +464,9 @@ class Autopilot(ContinueBaseModel):
# Update its description
async def update_description():
- step.description = await step.describe(self.continue_sdk.models)
+ description = await step.describe(self.continue_sdk.models)
+ if description is not None:
+ step.description = description
# Update subscribers with new description
await self.update_subscribers()
diff --git a/continuedev/src/continuedev/libs/util/edit_config.py b/continuedev/src/continuedev/libs/util/edit_config.py
index 7c8ee76f..f4285bc9 100644
--- a/continuedev/src/continuedev/libs/util/edit_config.py
+++ b/continuedev/src/continuedev/libs/util/edit_config.py
@@ -79,6 +79,8 @@ filtered_attrs = {
"llm",
}
+filtered_attrs_when_new = {"timeout", "prompt_templates"}
+
def escape_string(string: str) -> str:
return string.replace('"', '\\"').replace("'", "\\'")
@@ -90,20 +92,27 @@ def display_val(v: Any):
return str(v)
-def display_llm_class(llm):
- args = ", ".join(
+def display_llm_class(llm, new: bool = False):
+ sep = ",\n\t\t\t"
+ args = sep.join(
[
f"{k}={display_val(v)}"
for k, v in llm.dict().items()
if k not in filtered_attrs and v is not None
]
)
- return f"{llm.__class__.__name__}({args})"
+ return f"{llm.__class__.__name__}(\n\t\t\t{args}\n\t\t)"
-def create_obj_node(class_name: str, args: Dict[str, str]) -> redbaron.RedBaron:
+def create_obj_node(
+ class_name: str, args: Dict[str, str], tabs: int = 1
+) -> redbaron.RedBaron:
args = [f"{key}={value}" for key, value in args.items()]
- return redbaron.RedBaron(f"{class_name}({', '.join(args)})")[0]
+ t = "\t" * tabs
+ new_line = "\n\t" + t
+ sep = "," + new_line
+
+ return redbaron.RedBaron(f"{class_name}({new_line}{sep.join(args)}\n{t})")[0]
def create_string_node(string: str) -> redbaron.RedBaron:
diff --git a/continuedev/src/continuedev/plugins/steps/core/core.py b/continuedev/src/continuedev/plugins/steps/core/core.py
index 7766a887..97235e6f 100644
--- a/continuedev/src/continuedev/plugins/steps/core/core.py
+++ b/continuedev/src/continuedev/plugins/steps/core/core.py
@@ -183,33 +183,15 @@ class DefaultModelEditCodeStep(Step):
summary_prompt: str = "Please give brief a description of the changes made above using markdown bullet points. Be concise:"
async def describe(self, models: Models) -> Coroutine[str, None, None]:
- if self._previous_contents.strip() == self._new_contents.strip():
- description = "No edits were made"
- else:
- changes = "\n".join(
- difflib.ndiff(
- self._previous_contents.splitlines(),
- self._new_contents.splitlines(),
- )
- )
- description = await models.medium.complete(
- dedent(
- f"""\
- Diff summary: "{self.user_input}"
-
- ```diff
- {changes}
- ```
-
- {self.summary_prompt}"""
- )
- )
name = await models.medium.complete(
f"Write a very short title to describe this requested change (no quotes): '{self.user_input}'. This is the title:"
)
self.name = remove_quotes_and_escapes(name)
- return f"{remove_quotes_and_escapes(description)}"
+ if self._previous_contents.strip() == self._new_contents.strip():
+ return "No edits were made"
+ else:
+ return None
async def get_prompt_parts(
self, rif: RangeInFileWithContents, sdk: ContinueSDK, full_file_contents: str
@@ -821,6 +803,29 @@ Please output the code to be inserted at the cursor in order to fulfill the user
self.name = "Generating summary"
+ changes = "\n".join(
+ difflib.ndiff(
+ self._previous_contents.splitlines(),
+ self._new_contents.splitlines(),
+ )
+ )
+
+ self.description = ""
+ async for chunk in sdk.models.medium.stream_complete(
+ dedent(
+ f"""\
+ Diff summary: "{self.user_input}"
+
+ ```diff
+ {changes}
+ ```
+
+ {self.summary_prompt}"""
+ )
+ ):
+ self.description += chunk
+ await sdk.update_ui()
+
class EditFileStep(Step):
filepath: str
diff --git a/continuedev/src/continuedev/plugins/steps/setup_model.py b/continuedev/src/continuedev/plugins/steps/setup_model.py
index 2e1fdc14..3dae39c2 100644
--- a/continuedev/src/continuedev/plugins/steps/setup_model.py
+++ b/continuedev/src/continuedev/plugins/steps/setup_model.py
@@ -1,6 +1,8 @@
from ...core.main import Step
from ...core.sdk import ContinueSDK
from ...libs.util.paths import getConfigFilePath
+from ...models.filesystem import RangeInFile
+from ...models.main import Range
MODEL_CLASS_TO_MESSAGE = {
"OpenAI": "Obtain your OpenAI API key from [here](https://platform.openai.com/account/api-keys) and paste it into the `api_key` field at config.models.default.api_key in `config.py`. Then reload the VS Code window for changes to take effect.",
@@ -23,3 +25,12 @@ class SetupModelStep(Step):
self.description = MODEL_CLASS_TO_MESSAGE.get(
self.model_class, "Please finish setting up this model in `config.py`"
)
+
+ config_contents = await sdk.ide.readFile(getConfigFilePath())
+ start = config_contents.find("default=") + len("default=")
+ end = config_contents.find("unused=") - 1
+ range = Range.from_indices(config_contents, start, end)
+ range.end.line -= 1
+ await sdk.ide.highlightCode(
+ RangeInFile(filepath=getConfigFilePath(), range=range)
+ )
diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py
index 49541b76..770065ac 100644
--- a/continuedev/src/continuedev/server/gui.py
+++ b/continuedev/src/continuedev/server/gui.py
@@ -221,6 +221,7 @@ class GUIProtocolServer:
),
self.on_error,
)
+ posthog_logger.capture_event("set_system_message", {"system_message": message})
def set_temperature(self, temperature: float):
self.session.autopilot.continue_sdk.config.temperature = temperature
@@ -230,6 +231,7 @@ class GUIProtocolServer:
),
self.on_error,
)
+ posthog_logger.capture_event("set_temperature", {"temperature": temperature})
def set_model_for_role_from_index(self, role: str, index: int):
async def async_stuff():
@@ -242,7 +244,7 @@ class GUIProtocolServer:
await self.session.autopilot.continue_sdk.start_model(models.default)
# Set models in config.py
- JOINER = ", "
+ JOINER = ",\n\t\t"
models_args = {
"unused": f"[{JOINER.join([display_llm_class(llm) for llm in models.unused])}]",
("default" if role == "*" else role): display_llm_class(models.default),
@@ -285,9 +287,9 @@ class GUIProtocolServer:
if val is None:
continue # no pun intended
- models_args[role] = display_llm_class(val)
+ models_args[role] = display_llm_class(val, True)
- JOINER = ", "
+ JOINER = ",\n\t\t"
models_args[
"unused"
] = f"[{JOINER.join([display_llm_class(llm) for llm in unused_models])}]"
@@ -346,7 +348,7 @@ async def websocket_endpoint(
while AppStatus.should_exit is False:
message = await websocket.receive_text()
logger.debug(f"Received GUI message {message}")
- if type(message) is str:
+ if isinstance(message, str):
message = json.loads(message)
if "messageType" not in message or "data" not in message:
diff --git a/extension/react-app/src/components/Layout.tsx b/extension/react-app/src/components/Layout.tsx
index de8b86d8..065b77c6 100644
--- a/extension/react-app/src/components/Layout.tsx
+++ b/extension/react-app/src/components/Layout.tsx
@@ -3,7 +3,7 @@ import { defaultBorderRadius, secondaryDark, vscForeground } from ".";
import { Outlet } from "react-router-dom";
import Onboarding from "./Onboarding";
import TextDialog from "./TextDialog";
-import { useContext, useEffect } from "react";
+import { useContext, useEffect, useState } from "react";
import { GUIClientContext } from "../App";
import { useDispatch, useSelector } from "react-redux";
import { RootStore } from "../redux/store";
@@ -145,29 +145,37 @@ const Layout = () => {
<GridDiv>
<Outlet />
<Footer>
- {localStorage.getItem("hideFeature") === "true" || (
- <SparklesIcon
- className="mr-auto cursor-pointer"
- onClick={() => {
- localStorage.setItem("hideFeature", "true");
- }}
- onMouseEnter={() => {
- dispatch(
- setBottomMessage(
- "🎁 New Feature: Use ⌘⇧R automatically debug errors in the terminal (you can click the sparkle icon to make it go away)"
- )
- );
- }}
- onMouseLeave={() => {
- dispatch(setBottomMessage(undefined));
- }}
- width="1.3em"
- height="1.3em"
- color="yellow"
- />
+ {(localStorage.getItem("hideFeature") === "true" && false) || (
+ <div className="mr-auto flex gap-2 items-center">
+ <SparklesIcon
+ className="cursor-pointer"
+ onClick={() => {
+ localStorage.setItem("hideFeature", "true");
+ }}
+ onMouseEnter={() => {
+ dispatch(
+ setBottomMessage(
+ "🎁 New Feature: Use ⌘⇧R automatically debug errors in the terminal (you can click the sparkle icon to make it go away)"
+ )
+ );
+ }}
+ onMouseLeave={() => {
+ dispatch(
+ setBottomMessageCloseTimeout(
+ setTimeout(() => {
+ dispatch(setBottomMessage(undefined));
+ }, 2000)
+ )
+ );
+ }}
+ width="1.3em"
+ height="1.3em"
+ color="yellow"
+ />
+
+ <ModelSelect />
+ </div>
)}
-
- <ModelSelect />
<HeaderButtonWithText
onClick={() => {
client?.loadSession(undefined);
diff --git a/extension/react-app/src/components/ModelSelect.tsx b/extension/react-app/src/components/ModelSelect.tsx
index 317c164a..dc58da9e 100644
--- a/extension/react-app/src/components/ModelSelect.tsx
+++ b/extension/react-app/src/components/ModelSelect.tsx
@@ -1,18 +1,21 @@
import styled from "styled-components";
import {
defaultBorderRadius,
+ lightGray,
secondaryDark,
vscBackground,
vscForeground,
} from ".";
-import { useContext, useEffect } from "react";
+import { useContext } from "react";
import { GUIClientContext } from "../App";
import { RootStore } from "../redux/store";
-import { useSelector } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
+import { PlusIcon } from "@heroicons/react/24/outline";
+import { setDialogMessage, setShowDialog } from "../redux/slices/uiStateSlice";
const MODEL_INFO: { title: string; class: string; args: any }[] = [
{
- title: "gpt-4",
+ title: "OpenAI",
class: "OpenAI",
args: {
model: "gpt-4",
@@ -20,7 +23,7 @@ const MODEL_INFO: { title: string; class: string; args: any }[] = [
},
},
{
- title: "claude-2",
+ title: "Anthropic",
class: "AnthropicLLM",
args: {
model: "claude-2",
@@ -28,11 +31,6 @@ const MODEL_INFO: { title: string; class: string; args: any }[] = [
},
},
{
- title: "GGML",
- class: "GGML",
- args: {},
- },
- {
title: "Ollama",
class: "Ollama",
args: {
@@ -40,6 +38,14 @@ const MODEL_INFO: { title: string; class: string; args: any }[] = [
},
},
{
+ title: "TogetherAI",
+ class: "TogetherLLM",
+ args: {
+ model: "togethercomputer/CodeLlama-13b-Instruct",
+ api_key: "<TOGETHER_API_KEY>",
+ },
+ },
+ {
title: "Replicate",
class: "ReplicateLLM",
args: {
@@ -49,20 +55,19 @@ const MODEL_INFO: { title: string; class: string; args: any }[] = [
},
},
{
- title: "TogetherAI",
- class: "TogetherLLM",
- args: {
- model: "togethercomputer/CodeLlama-13b-Instruct",
- api_key: "<TOGETHER_API_KEY>",
- },
- },
- {
title: "llama.cpp",
class: "LlamaCpp",
args: {},
},
{
- title: "gpt-4 (limited free usage)",
+ title: "Other OpenAI-compatible API",
+ class: "GGML",
+ args: {
+ server_url: "<SERVER_URL>",
+ },
+ },
+ {
+ title: "Continue Free Trial (gpt-4)",
class: "MaybeProxyOpenAI",
args: {
model: "gpt-4",
@@ -70,21 +75,56 @@ const MODEL_INFO: { title: string; class: string; args: any }[] = [
},
];
+const GridDiv = styled.div`
+ display: grid;
+ grid-template-columns: 1fr auto;
+ align-items: center;
+ border: 0.5px solid ${lightGray};
+ border-radius: ${defaultBorderRadius};
+ overflow: hidden;
+`;
+
const Select = styled.select`
border: none;
- width: 25vw;
- background-color: ${secondaryDark};
+ max-width: 25vw;
+ background-color: ${vscBackground};
color: ${vscForeground};
- border-radius: ${defaultBorderRadius};
padding: 6px;
max-height: 35vh;
overflow: scroll;
cursor: pointer;
- margin-right: auto;
&:focus {
outline: none;
}
+ &:hover {
+ background-color: ${secondaryDark};
+ }
+`;
+
+const StyledPlusIcon = styled(PlusIcon)`
+ cursor: pointer;
+ margin: 0px;
+ padding-left: 4px;
+ padding-right: 4px;
+ height: 100%;
+
+ &:hover {
+ background-color: ${secondaryDark};
+ }
+ border-left: 0.5px solid ${lightGray};
+`;
+
+const NewProviderDiv = styled.div`
+ cursor: pointer;
+ padding: 8px;
+ padding-left: 16px;
+ padding-right: 16px;
+ border-top: 0.5px solid ${lightGray};
+
+ &:hover {
+ background-color: ${secondaryDark};
+ }
`;
function modelSelectTitle(model: any): string {
@@ -99,6 +139,7 @@ function modelSelectTitle(model: any): string {
}
function ModelSelect(props: {}) {
+ const dispatch = useDispatch();
const client = useContext(GUIClientContext);
const defaultModel = useSelector(
(state: RootStore) => (state.serverState.config as any)?.models?.default
@@ -108,23 +149,20 @@ function ModelSelect(props: {}) {
);
return (
- <Select
- value={JSON.stringify({
- t: "default",
- idx: -1,
- })}
- defaultValue={0}
- onChange={(e) => {
- const value = JSON.parse(e.target.value);
- if (value.t === "unused") {
- client?.setModelForRoleFromIndex("*", value.idx);
- } else if (value.t === "new") {
- const model = MODEL_INFO[value.idx];
- client?.addModelForRole("*", model.class, model.args);
- }
- }}
- >
- <optgroup label="My Saved Models">
+ <GridDiv>
+ <Select
+ value={JSON.stringify({
+ t: "default",
+ idx: -1,
+ })}
+ defaultValue={0}
+ onChange={(e) => {
+ const value = JSON.parse(e.target.value);
+ if (value.t === "unused") {
+ client?.setModelForRoleFromIndex("*", value.idx);
+ }
+ }}
+ >
{defaultModel && (
<option
value={JSON.stringify({
@@ -147,22 +185,40 @@ function ModelSelect(props: {}) {
</option>
);
})}
- </optgroup>
- <optgroup label="Add New Model">
- {MODEL_INFO.map((model, idx) => {
- return (
- <option
- value={JSON.stringify({
- t: "new",
- idx,
- })}
- >
- {model.title}
- </option>
+ </Select>
+
+ <StyledPlusIcon
+ width="1.3em"
+ height="1.3em"
+ onClick={() => {
+ dispatch(
+ setDialogMessage(
+ <div>
+ <div className="text-lg font-bold p-2">
+ Setup a new model provider
+ </div>
+ <br />
+ {MODEL_INFO.map((model, idx) => {
+ return (
+ <NewProviderDiv
+ onClick={() => {
+ const model = MODEL_INFO[idx];
+ client?.addModelForRole("*", model.class, model.args);
+ dispatch(setShowDialog(false));
+ }}
+ >
+ {model.title}
+ </NewProviderDiv>
+ );
+ })}
+ <br />
+ </div>
+ )
);
- })}
- </optgroup>
- </Select>
+ dispatch(setShowDialog(true));
+ }}
+ />
+ </GridDiv>
);
}
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index 44d25ae6..a9fcbb8f 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -25,12 +25,12 @@ const Dialog = styled.div`
color: ${vscForeground};
background-color: ${vscBackground};
border-radius: 8px;
- padding: 8px;
display: flex;
flex-direction: column;
box-shadow: 0 0 10px 0 ${vscForeground};
margin: auto;
word-wrap: break-word;
+ overflow: hidden;
`;
const TextArea = styled.textarea`
diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx
index 228c3530..866fef58 100644
--- a/extension/react-app/src/components/UserInputContainer.tsx
+++ b/extension/react-app/src/components/UserInputContainer.tsx
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import {
defaultBorderRadius,
+ lightGray,
secondaryDark,
vscBackground,
vscForeground,
@@ -28,6 +29,9 @@ const StyledDiv = styled.div`
padding: 8px;
padding-top: 0px;
padding-bottom: 0px;
+
+ border-bottom: 0.5px solid ${lightGray};
+ border-top: 0.5px solid ${lightGray};
`;
const DeleteButtonDiv = styled.div`
diff --git a/extension/react-app/src/components/dialogs/AddContextGroupDialog.tsx b/extension/react-app/src/components/dialogs/AddContextGroupDialog.tsx
index f6c7c626..9cd0a95e 100644
--- a/extension/react-app/src/components/dialogs/AddContextGroupDialog.tsx
+++ b/extension/react-app/src/components/dialogs/AddContextGroupDialog.tsx
@@ -26,7 +26,7 @@ function AddContextGroupDialog({
};
return (
- <div>
+ <div className="p-4">
<TextInput
defaultValue="My Context Group"
type="text"
diff --git a/extension/react-app/src/components/dialogs/SelectContextGroupDialog.tsx b/extension/react-app/src/components/dialogs/SelectContextGroupDialog.tsx
index b8fc2bb7..44095fc0 100644
--- a/extension/react-app/src/components/dialogs/SelectContextGroupDialog.tsx
+++ b/extension/react-app/src/components/dialogs/SelectContextGroupDialog.tsx
@@ -57,7 +57,7 @@ function SelectContextGroupDialog() {
const client = useContext(GUIClientContext);
return (
- <div className="px-4">
+ <div className="p-4">
<h2>Saved Context Groups</h2>
{savedContextGroups && Object.keys(savedContextGroups).length > 0 ? (
diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx
index 99e7cb7b..cb62f7ed 100644
--- a/extension/react-app/src/pages/gui.tsx
+++ b/extension/react-app/src/pages/gui.tsx
@@ -277,7 +277,7 @@ function GUI(props: GUIProps) {
if (currentCount === 100) {
dispatch(
setDialogMessage(
- <div className="text-center">
+ <div className="text-center p-4">
👋 Thanks for using Continue. We are a beta product and love
working closely with our first users. If you're interested in
speaking, enter your name and email. We won't use this
@@ -293,7 +293,7 @@ function GUI(props: GUIProps) {
});
dispatch(
setDialogMessage(
- <div className="text-center">
+ <div className="text-center p-4">
Thanks! We'll be in touch soon.
</div>
)