diff options
author | Nate Sesti <sestinj@gmail.com> | 2023-09-12 00:59:20 -0700 |
---|---|---|
committer | Nate Sesti <sestinj@gmail.com> | 2023-09-12 00:59:20 -0700 |
commit | 331b2adcb6f8d962e4ed19292fd2ab5838ba479e (patch) | |
tree | 055989d31f18f18971d9f8e3e5764b59ed0c2be5 /continuedev | |
parent | e9afb41bed9a723876cf1cf95d636b2ea498a6b3 (diff) | |
download | sncontinue-331b2adcb6f8d962e4ed19292fd2ab5838ba479e.tar.gz sncontinue-331b2adcb6f8d962e4ed19292fd2ab5838ba479e.tar.bz2 sncontinue-331b2adcb6f8d962e4ed19292fd2ab5838ba479e.zip |
docs: :memo: major docs improvements
Diffstat (limited to 'continuedev')
-rw-r--r-- | continuedev/src/continuedev/core/config.py | 83 | ||||
-rw-r--r-- | continuedev/src/continuedev/core/context.py | 46 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/__init__.py | 130 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/anthropic.py | 18 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/ggml.py | 39 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/hf_inference_api.py | 44 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/hf_tgi.py | 10 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/llamacpp.py | 63 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/maybe_proxy_openai.py | 25 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/ollama.py | 20 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/openai.py | 57 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/queued.py | 21 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/replicate.py | 24 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/text_gen_interface.py | 31 | ||||
-rw-r--r-- | continuedev/src/continuedev/libs/llm/together.py | 33 | ||||
-rw-r--r-- | continuedev/src/continuedev/models/reference/test.py | 110 |
16 files changed, 582 insertions, 172 deletions
diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py index b513e22a..e8b2c579 100644 --- a/continuedev/src/continuedev/core/config.py +++ b/continuedev/src/continuedev/core/config.py @@ -1,6 +1,6 @@ from typing import Dict, List, Optional, Type -from pydantic import BaseModel, validator +from pydantic import BaseModel, Field, validator from ..libs.llm.maybe_proxy_openai import MaybeProxyOpenAI from .context import ContextProvider @@ -31,31 +31,68 @@ class CustomCommand(BaseModel): class ContinueConfig(BaseModel): """ - A pydantic class for the continue config file. + Continue can be deeply customized by editing the `ContinueConfig` object in `~/.continue/config.py` (`%userprofile%\.continue\config.py` for Windows) on your machine. This class is instantiated from the config file for every new session. """ - steps_on_startup: List[Step] = [] - disallowed_steps: Optional[List[str]] = [] - allow_anonymous_telemetry: Optional[bool] = True - models: Models = Models( - default=MaybeProxyOpenAI(model="gpt-4"), - medium=MaybeProxyOpenAI(model="gpt-3.5-turbo"), + steps_on_startup: List[Step] = Field( + [], + description="Steps that will be automatically run at the beginning of a new session", + ) + disallowed_steps: Optional[List[str]] = Field( + [], + description="Steps that are not allowed to be run, and will be skipped if attempted", + ) + allow_anonymous_telemetry: Optional[bool] = Field( + True, + description="If this field is set to True, we will collect anonymous telemetry as described in the documentation page on telemetry. If set to False, we will not collect any data.", + ) + models: Models = Field( + Models( + default=MaybeProxyOpenAI(model="gpt-4"), + medium=MaybeProxyOpenAI(model="gpt-3.5-turbo"), + ), + description="Configuration for the models used by Continue. Read more about how to configure models in the documentation.", + ) + temperature: Optional[float] = Field( + 0.5, + description="The temperature parameter for sampling from the LLM. Higher temperatures will result in more random output, while lower temperatures will result in more predictable output. This value ranges from 0 to 1.", + ) + custom_commands: Optional[List[CustomCommand]] = Field( + [ + CustomCommand( + name="test", + description="This is an example custom command. Use /config to edit it and create more", + prompt="Write a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.", + ) + ], + description="An array of custom commands that allow you to reuse prompts. Each has name, description, and prompt properties. When you enter /<name> in the text input, it will act as a shortcut to the prompt.", + ) + slash_commands: Optional[List[SlashCommand]] = Field( + [], + description="An array of slash commands that let you map custom Steps to a shortcut.", + ) + on_traceback: Optional[Step] = Field( + None, + description="The step that will be run when a traceback is detected (when you use the shortcut cmd+shift+R)", + ) + system_message: Optional[str] = Field( + None, description="A system message that will always be followed by the LLM" + ) + policy_override: Optional[Policy] = Field( + None, + description="A Policy object that can be used to override the default behavior of Continue, for example in order to build custom agents that take multiple steps at a time.", + ) + context_providers: List[ContextProvider] = Field( + [], + description="A list of ContextProvider objects that can be used to provide context to the LLM by typing '@'. Read more about ContextProviders in the documentation.", + ) + user_token: Optional[str] = Field( + None, description="An optional token to identify the user." + ) + data_server_url: Optional[str] = Field( + "https://us-west1-autodebug.cloudfunctions.net", + description="The URL of the server where development data is sent. No data is sent unless a valid user token is provided.", ) - temperature: Optional[float] = 0.5 - custom_commands: Optional[List[CustomCommand]] = [ - CustomCommand( - name="test", - description="This is an example custom command. Use /config to edit it and create more", - prompt="Write a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.", - ) - ] - slash_commands: Optional[List[SlashCommand]] = [] - on_traceback: Optional[Step] = None - system_message: Optional[str] = None - policy_override: Optional[Policy] = None - context_providers: List[ContextProvider] = [] - user_token: Optional[str] = None - data_server_url: Optional[str] = "https://us-west1-autodebug.cloudfunctions.net" @validator("temperature", pre=True) def temperature_validator(cls, v): diff --git a/continuedev/src/continuedev/core/context.py b/continuedev/src/continuedev/core/context.py index c9768a97..f2658602 100644 --- a/continuedev/src/continuedev/core/context.py +++ b/continuedev/src/continuedev/core/context.py @@ -4,7 +4,7 @@ from abc import abstractmethod from typing import Awaitable, Callable, Dict, List from meilisearch_python_async import Client -from pydantic import BaseModel +from pydantic import BaseModel, Field from ..libs.util.create_async_task import create_async_task from ..libs.util.devdata import dev_data_logger @@ -37,17 +37,39 @@ class ContextProvider(BaseModel): When you hit enter on an option, the context provider will add that item to the autopilot's list of context (which is all stored in the ContextManager object). """ - title: str - sdk: ContinueSDK = None - delete_documents: Callable[[List[str]], Awaitable] = None - update_documents: Callable[[List[ContextItem], str], Awaitable] = None - - display_title: str - description: str - dynamic: bool - requires_query: bool = False - - selected_items: List[ContextItem] = [] + title: str = Field( + ..., + description="The title of the ContextProvider. This is what must be typed in the input to trigger the ContextProvider.", + ) + sdk: ContinueSDK = Field( + None, description="The ContinueSDK instance accessible by the ContextProvider" + ) + delete_documents: Callable[[List[str]], Awaitable] = Field( + None, description="Function to delete documents" + ) + update_documents: Callable[[List[ContextItem], str], Awaitable] = Field( + None, description="Function to update documents" + ) + + display_title: str = Field( + ..., + description="The display title of the ContextProvider shown in the dropdown menu", + ) + description: str = Field( + ..., + description="A description of the ContextProvider displayed in the dropdown menu", + ) + dynamic: bool = Field( + ..., description="Indicates whether the ContextProvider is dynamic" + ) + requires_query: bool = Field( + False, + description="Indicates whether the ContextProvider requires a query. For example, the SearchContextProvider requires you to type '@search <STRING_TO_SEARCH>'. This will change the behavior of the UI so that it can indicate the expectation for a query.", + ) + + selected_items: List[ContextItem] = Field( + [], description="List of selected items in the ContextProvider" + ) def dict(self, *args, **kwargs): original_dict = super().dict(*args, **kwargs) diff --git a/continuedev/src/continuedev/libs/llm/__init__.py b/continuedev/src/continuedev/libs/llm/__init__.py index 653d2d6b..11d81b3f 100644 --- a/continuedev/src/continuedev/libs/llm/__init__.py +++ b/continuedev/src/continuedev/libs/llm/__init__.py @@ -1,6 +1,6 @@ from typing import Any, Callable, Coroutine, Dict, Generator, List, Optional, Union -from pydantic import validator +from pydantic import Field, validator from ...core.main import ChatMessage from ...libs.util.devdata import dev_data_logger @@ -26,63 +26,101 @@ class CompletionOptions(ContinueBaseModel): def ignore_none_and_set_default(cls, value, field): return value if value is not None else field.default - model: str = None - "The model name" - temperature: Optional[float] = None - "The temperature of the completion." - - top_p: Optional[float] = None - "The top_p of the completion." - - top_k: Optional[int] = None - "The top_k of the completion." - - presence_penalty: Optional[float] = None - "The presence penalty of the completion." - - frequency_penalty: Optional[float] = None - "The frequency penalty of the completion." - - stop: Optional[List[str]] = None - "The stop tokens of the completion." - - max_tokens: int = DEFAULT_MAX_TOKENS - "The maximum number of tokens to generate." - - functions: Optional[List[Any]] = None - "The functions/tools to make available to the model." + model: Optional[str] = Field(None, description="The model name") + temperature: Optional[float] = Field( + None, description="The temperature of the completion." + ) + top_p: Optional[float] = Field(None, description="The top_p of the completion.") + top_k: Optional[int] = Field(None, description="The top_k of the completion.") + presence_penalty: Optional[float] = Field( + None, description="The presence penalty of the completion." + ) + frequency_penalty: Optional[float] = Field( + None, description="The frequency penalty of the completion." + ) + stop: Optional[List[str]] = Field( + None, description="The stop tokens of the completion." + ) + max_tokens: int = Field( + DEFAULT_MAX_TOKENS, description="The maximum number of tokens to generate." + ) + functions: Optional[List[Any]] = Field( + None, description="The functions/tools to make available to the model." + ) class LLM(ContinueBaseModel): - title: Optional[str] = None - system_message: Optional[str] = None - - context_length: int = 2048 - "The maximum context length of the LLM in tokens, as counted by count_tokens." - - unique_id: Optional[str] = None - "The unique ID of the user." - - model: str - "The model name" + title: Optional[str] = Field( + None, + description="A title that will identify this model in the model selection dropdown", + ) + system_message: Optional[str] = Field( + None, description="A system message that will always be followed by the LLM" + ) - timeout: Optional[int] = 300 - "The timeout for the request in seconds." + context_length: int = Field( + 2048, + description="The maximum context length of the LLM in tokens, as counted by count_tokens.", + ) - prompt_templates: dict = {} + unique_id: Optional[str] = Field(None, description="The unique ID of the user.") + model: str = Field( + ..., description="The name of the model to be used (e.g. gpt-4, codellama)" + ) - template_messages: Optional[Callable[[List[Dict[str, str]]], str]] = None - "A function that takes a list of messages and returns a prompt." + timeout: Optional[int] = Field( + 300, + description="Set the timeout for each request to the LLM. If you are running a local LLM that takes a while to respond, you might want to set this to avoid timeouts.", + ) + prompt_templates: dict = Field( + {}, + description='A dictionary of prompt templates that can be used to customize the behavior of the LLM in certain situations. For example, set the "edit" key in order to change the prompt that is used for the /edit slash command. Each value in the dictionary is a string templated in mustache syntax, and filled in at runtime with the variables specific to the situation. See the documentation for more information.', + ) - write_log: Optional[Callable[[str], None]] = None - "A function that takes a string and writes it to the log." + template_messages: Optional[Callable[[List[Dict[str, str]]], str]] = Field( + None, + description="A function that takes a list of messages and returns a prompt. This ensures that models like llama2, which are trained on specific chat formats, will always recieve input in that format.", + ) + write_log: Optional[Callable[[str], None]] = Field( + None, + description="A function that is called upon every prompt and completion, by default to log to the file which can be viewed by clicking on the magnifying glass.", + ) - api_key: Optional[str] = None - "The API key for the LLM provider." + api_key: Optional[str] = Field( + None, description="The API key for the LLM provider." + ) class Config: arbitrary_types_allowed = True extra = "allow" + fields = { + "title": { + "description": "A title that will identify this model in the model selection dropdown" + }, + "system_message": { + "description": "A system message that will always be followed by the LLM" + }, + "context_length": { + "description": "The maximum context length of the LLM in tokens, as counted by count_tokens." + }, + "unique_id": {"description": "The unique ID of the user."}, + "model": { + "description": "The name of the model to be used (e.g. gpt-4, codellama)" + }, + "timeout": { + "description": "Set the timeout for each request to the LLM. If you are running a local LLM that takes a while to respond, you might want to set this to avoid timeouts." + }, + "prompt_templates": { + "description": 'A dictionary of prompt templates that can be used to customize the behavior of the LLM in certain situations. For example, set the "edit" key in order to change the prompt that is used for the /edit slash command. Each value in the dictionary is a string templated in mustache syntax, and filled in at runtime with the variables specific to the situation. See the documentation for more information.' + }, + "template_messages": { + "description": "A function that takes a list of messages and returns a prompt. This ensures that models like llama2, which are trained on specific chat formats, will always recieve input in that format." + }, + "write_log": { + "description": "A function that is called upon every prompt and completion, by default to log to the file which can be viewed by clicking on the magnifying glass." + }, + "api_key": {"description": "The API key for the LLM provider."}, + } def dict(self, **kwargs): original_dict = super().dict(**kwargs) diff --git a/continuedev/src/continuedev/libs/llm/anthropic.py b/continuedev/src/continuedev/libs/llm/anthropic.py index 70a0868c..d3b773e4 100644 --- a/continuedev/src/continuedev/libs/llm/anthropic.py +++ b/continuedev/src/continuedev/libs/llm/anthropic.py @@ -7,6 +7,24 @@ from .prompts.chat import anthropic_template_messages class AnthropicLLM(LLM): + """ + Import the `AnthropicLLM` class and set it as the default model: + + ```python + from continuedev.src.continuedev.libs.llm.anthropic import AnthropicLLM + + config = ContinueConfig( + ... + models=Models( + default=AnthropicLLM(api_key="<API_KEY>", model="claude-2") + ) + ) + ``` + + Claude 2 is not yet publicly released. You can request early access [here](https://www.anthropic.com/earlyaccess). + + """ + api_key: str "Anthropic API key" diff --git a/continuedev/src/continuedev/libs/llm/ggml.py b/continuedev/src/continuedev/libs/llm/ggml.py index dd1bdec2..4f942bd6 100644 --- a/continuedev/src/continuedev/libs/llm/ggml.py +++ b/continuedev/src/continuedev/libs/llm/ggml.py @@ -3,6 +3,7 @@ import ssl from typing import Any, Callable, Coroutine, Dict, List, Optional import aiohttp +from pydantic import Field from ...core.main import ChatMessage from ..llm import LLM @@ -14,10 +15,40 @@ from .prompts.edit import simplified_edit_prompt class GGML(LLM): - server_url: str = "http://localhost:8000" - verify_ssl: Optional[bool] = None - ca_bundle_path: str = None - model: str = "ggml" + """ + See our [5 minute quickstart](https://github.com/continuedev/ggml-server-example) to run any model locally with ggml. While these models don't yet perform as well, they are free, entirely private, and run offline. + + Once the model is running on localhost:8000, change `~/.continue/config.py` to look like this: + + ```python + from continuedev.src.continuedev.libs.llm.ggml import GGML + + config = ContinueConfig( + ... + models=Models( + default=GGML( + max_context_length=2048, + server_url="http://localhost:8000") + ) + ) + ``` + """ + + server_url: str = Field( + "http://localhost:8000", + description="URL of the OpenAI-compatible server where the model is being served", + ) + verify_ssl: Optional[bool] = Field( + None, + description="Whether SSL certificates should be verified when making the HTTP request", + ) + ca_bundle_path: str = Field( + None, + description="Path to a custom CA bundle to use when making the HTTP request", + ) + model: str = Field( + "ggml", description="The name of the model to use (optional for the GGML class)" + ) template_messages: Optional[ Callable[[List[Dict[str, str]]], str] diff --git a/continuedev/src/continuedev/libs/llm/hf_inference_api.py b/continuedev/src/continuedev/libs/llm/hf_inference_api.py index b8fc49a9..81c10e8e 100644 --- a/continuedev/src/continuedev/libs/llm/hf_inference_api.py +++ b/continuedev/src/continuedev/libs/llm/hf_inference_api.py @@ -1,20 +1,47 @@ from typing import Callable, Dict, List -from ..llm import LLM, CompletionOptions from huggingface_hub import InferenceClient +from pydantic import Field + +from ..llm import LLM, CompletionOptions from .prompts.chat import llama2_template_messages from .prompts.edit import simplified_edit_prompt class HuggingFaceInferenceAPI(LLM): - model: str = "Hugging Face Inference API" - hf_token: str - endpoint_url: str = None + """ + Hugging Face Inference API is a great option for newly released language models. Sign up for an account and add billing [here](https://huggingface.co/settings/billing), access the Inference Endpoints [here](https://ui.endpoints.huggingface.co), click on “New endpoint”, and fill out the form (e.g. select a model like [WizardCoder-Python-34B-V1.0](https://huggingface.co/WizardLM/WizardCoder-Python-34B-V1.0)), and then deploy your model by clicking “Create Endpoint”. Change `~/.continue/config.py` to look like this: + + ```python + from continuedev.src.continuedev.core.models import Models + from continuedev.src.continuedev.libs.llm.hf_inference_api import HuggingFaceInferenceAPI - template_messages: Callable[[List[Dict[str, str]]], str] | None = llama2_template_messages + config = ContinueConfig( + ... + models=Models( + default=HuggingFaceInferenceAPI( + endpoint_url: "<INFERENCE_API_ENDPOINT_URL>", + hf_token: "<HUGGING_FACE_TOKEN>", + ) + ) + ``` + """ + + model: str = Field( + "Hugging Face Inference API", + description="The name of the model to use (optional for the HuggingFaceInferenceAPI class)", + ) + hf_token: str = Field(..., description="Your Hugging Face API token") + endpoint_url: str = Field( + None, description="Your Hugging Face Inference API endpoint URL" + ) + + template_messages: Callable[ + [List[Dict[str, str]]], str + ] | None = llama2_template_messages prompt_templates = { - "edit": simplified_edit_prompt, + "edit": simplified_edit_prompt, } class Config: @@ -34,9 +61,8 @@ class HuggingFaceInferenceAPI(LLM): del args["model"] return args - async def _stream_complete(self, prompt, options): - args = self.collect_args(options) + self.collect_args(options) client = InferenceClient(self.endpoint_url, token=self.hf_token) @@ -50,4 +76,4 @@ class HuggingFaceInferenceAPI(LLM): if options.stop is not None: if r.token.text in options.stop: break - yield r.token.text
\ No newline at end of file + yield r.token.text diff --git a/continuedev/src/continuedev/libs/llm/hf_tgi.py b/continuedev/src/continuedev/libs/llm/hf_tgi.py index 508ebe87..df10cb9f 100644 --- a/continuedev/src/continuedev/libs/llm/hf_tgi.py +++ b/continuedev/src/continuedev/libs/llm/hf_tgi.py @@ -2,6 +2,7 @@ import json from typing import Any, Callable, List, Optional import aiohttp +from pydantic import Field from ...core.main import ChatMessage from ..llm import LLM, CompletionOptions @@ -11,8 +12,13 @@ from .prompts.edit import simplified_edit_prompt class HuggingFaceTGI(LLM): model: str = "huggingface-tgi" - server_url: str = "http://localhost:8080" - verify_ssl: Optional[bool] = None + server_url: str = Field( + "http://localhost:8080", description="URL of your TGI server" + ) + verify_ssl: Optional[bool] = Field( + None, + description="Whether SSL certificates should be verified when making the HTTP request", + ) template_messages: Callable[[List[ChatMessage]], str] = code_llama_template_messages diff --git a/continuedev/src/continuedev/libs/llm/llamacpp.py b/continuedev/src/continuedev/libs/llm/llamacpp.py index 3596fd99..10dfcad8 100644 --- a/continuedev/src/continuedev/libs/llm/llamacpp.py +++ b/continuedev/src/continuedev/libs/llm/llamacpp.py @@ -1,8 +1,8 @@ -import asyncio import json from typing import Any, Callable, Dict, Optional import aiohttp +from pydantic import Field from ..llm import LLM from .prompts.chat import llama2_template_messages @@ -10,13 +10,40 @@ from .prompts.edit import simplified_edit_prompt class LlamaCpp(LLM): - model: str = "llamacpp" - server_url: str = "http://localhost:8080" - verify_ssl: Optional[bool] = None + """ + Run the llama.cpp server binary to start the API server. If running on a remote server, be sure to set host to 0.0.0.0: + + ```shell + .\server.exe -c 4096 --host 0.0.0.0 -t 16 --mlock -m models\meta\llama\codellama-7b-instruct.Q8_0.gguf + ``` + + After it's up and running, change `~/.continue/config.py` to look like this: + + ```python + from continuedev.src.continuedev.libs.llm.llamacpp import LlamaCpp - llama_cpp_args: Dict[str, Any] = {"stop": ["[INST]"]} + config = ContinueConfig( + ... + models=Models( + default=LlamaCpp( + max_context_length=4096, + server_url="http://localhost:8080") + ) + ) + ``` + """ + + model: str = "llamacpp" + server_url: str = Field("http://localhost:8080", description="URL of the server") + verify_ssl: Optional[bool] = Field( + None, + description="Whether SSL certificates should be verified when making the HTTP request", + ) - use_command: Optional[str] = None + llama_cpp_args: Dict[str, Any] = Field( + {"stop": ["[INST]"]}, + description="A list of additional arguments to pass to llama.cpp. See [here](https://github.com/ggerganov/llama.cpp/tree/master/examples/server#api-endpoints) for the complete catalog of options.", + ) template_messages: Callable = llama2_template_messages prompt_templates = { @@ -42,23 +69,6 @@ class LlamaCpp(LLM): return args - async def stream_from_main(self, prompt: str): - cmd = self.use_command.split(" ") + ["-p", prompt] - process = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE - ) - - total = "" - async for line in process.stdout: - chunk = line.decode().strip() - if "llama_print_timings" in total + chunk: - process.terminate() - return - total += chunk - yield chunk - - await process.wait() - async def _stream_complete(self, prompt, options): args = self.collect_args(options) headers = {"Content-Type": "application/json"} @@ -79,10 +89,5 @@ class LlamaCpp(LLM): continue yield json.loads(content[6:])["content"] - async def command_generator(): - async for line in self.stream_from_main(prompt): - yield line - - generator = command_generator if self.use_command else server_generator - async for chunk in generator(): + async for chunk in server_generator(): yield chunk diff --git a/continuedev/src/continuedev/libs/llm/maybe_proxy_openai.py b/continuedev/src/continuedev/libs/llm/maybe_proxy_openai.py index 07e27349..69def48e 100644 --- a/continuedev/src/continuedev/libs/llm/maybe_proxy_openai.py +++ b/continuedev/src/continuedev/libs/llm/maybe_proxy_openai.py @@ -7,6 +7,31 @@ from .proxy_server import ProxyServer class MaybeProxyOpenAI(LLM): + """ + With the `MaybeProxyOpenAI` `LLM`, new users can try out Continue with GPT-4 using a proxy server that securely makes calls to OpenAI using our API key. Continue should just work the first time you install the extension in VS Code. + + Once you are using Continue regularly though, you will need to add an OpenAI API key that has access to GPT-4 by following these steps: + + 1. Copy your API key from https://platform.openai.com/account/api-keys + 2. Open `~/.continue/config.py`. You can do this by using the '/config' command in Continue + 3. Change the default LLMs to look like this: + + ```python + API_KEY = "<API_KEY>" + config = ContinueConfig( + ... + models=Models( + default=MaybeProxyOpenAI(model="gpt-4", api_key=API_KEY), + medium=MaybeProxyOpenAI(model="gpt-3.5-turbo", api_key=API_KEY) + ) + ) + ``` + + The `MaybeProxyOpenAI` class will automatically switch to using your API key instead of ours. If you'd like to explicitly use one or the other, you can use the `ProxyServer` or `OpenAI` classes instead. + + These classes support any models available through the OpenAI API, assuming your API key has access, including "gpt-4", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", and "gpt-4-32k". + """ + api_key: Optional[str] = None llm: Optional[LLM] = None diff --git a/continuedev/src/continuedev/libs/llm/ollama.py b/continuedev/src/continuedev/libs/llm/ollama.py index d0da281a..b699398b 100644 --- a/continuedev/src/continuedev/libs/llm/ollama.py +++ b/continuedev/src/continuedev/libs/llm/ollama.py @@ -2,6 +2,7 @@ import json from typing import Callable import aiohttp +from pydantic import Field from ..llm import LLM from .prompts.chat import llama2_template_messages @@ -9,8 +10,25 @@ from .prompts.edit import simplified_edit_prompt class Ollama(LLM): + """ + [Ollama](https://ollama.ai/) is a Mac application that makes it easy to locally run open-source models, including Llama-2. Download the app from the website, and it will walk you through setup in a couple of minutes. You can also read more in their [README](https://github.com/jmorganca/ollama). Continue can then be configured to use the `Ollama` LLM class: + + ```python + from continuedev.src.continuedev.libs.llm.ollama import Ollama + + config = ContinueConfig( + ... + models=Models( + default=Ollama(model="llama2") + ) + ) + ``` + """ + model: str = "llama2" - server_url: str = "http://localhost:11434" + server_url: str = Field( + "http://localhost:11434", description="URL of the Ollama server" + ) _client_session: aiohttp.ClientSession = None diff --git a/continuedev/src/continuedev/libs/llm/openai.py b/continuedev/src/continuedev/libs/llm/openai.py index 70594973..744c07d2 100644 --- a/continuedev/src/continuedev/libs/llm/openai.py +++ b/continuedev/src/continuedev/libs/llm/openai.py @@ -30,34 +30,59 @@ class OpenAI(LLM): """ The OpenAI class can be used to access OpenAI models like gpt-4 and gpt-3.5-turbo. - If you are running a local model with an OpenAI-compatible API, you can also use the OpenAI class by changing the `api_base` argument. + If you are locally serving a model that uses an OpenAI-compatible server, you can simply change the `api_base` in the `OpenAI` class like this: + + ```python + from continuedev.src.continuedev.libs.llm.openai import OpenAI + + config = ContinueConfig( + ... + models=Models( + default=OpenAI( + api_key="EMPTY", + model="<MODEL_NAME>", + api_base="http://localhost:8000", # change to your server + ) + ) + ) + ``` + + Options for serving models locally with an OpenAI-compatible server include: + + - [text-gen-webui](https://github.com/oobabooga/text-generation-webui/tree/main/extensions/openai#setup--installation) + - [FastChat](https://github.com/lm-sys/FastChat/blob/main/docs/openai_api.md) + - [LocalAI](https://localai.io/basics/getting_started/) + - [llama-cpp-python](https://github.com/abetlen/llama-cpp-python#web-server) """ api_key: str = Field( + ..., description="OpenAI API key", ) - "OpenAI API key" - verify_ssl: Optional[bool] = None - "Whether to verify SSL certificates for requests." + verify_ssl: Optional[bool] = Field( + None, description="Whether to verify SSL certificates for requests." + ) - ca_bundle_path: Optional[str] = None - "Path to CA bundle to use for requests." + ca_bundle_path: Optional[str] = Field( + None, description="Path to CA bundle to use for requests." + ) - proxy: Optional[str] = None - "Proxy URL to use for requests." + proxy: Optional[str] = Field(None, description="Proxy URL to use for requests.") - api_base: Optional[str] = None - "OpenAI API base URL." + api_base: Optional[str] = Field(None, description="OpenAI API base URL.") - api_type: Optional[Literal["azure", "openai"]] = None - "OpenAI API type." + api_type: Optional[Literal["azure", "openai"]] = Field( + None, description="OpenAI API type." + ) - api_version: Optional[str] = None - "OpenAI API version. For use with Azure OpenAI Service." + api_version: Optional[str] = Field( + None, description="OpenAI API version. For use with Azure OpenAI Service." + ) - engine: Optional[str] = None - "OpenAI engine. For use with Azure OpenAI Service." + engine: Optional[str] = Field( + None, description="OpenAI engine. For use with Azure OpenAI Service." + ) async def start( self, unique_id: Optional[str] = None, write_log: Callable[[str], None] = None diff --git a/continuedev/src/continuedev/libs/llm/queued.py b/continuedev/src/continuedev/libs/llm/queued.py index 6dbaaa64..11fd74d6 100644 --- a/continuedev/src/continuedev/libs/llm/queued.py +++ b/continuedev/src/continuedev/libs/llm/queued.py @@ -1,12 +1,31 @@ import asyncio from typing import Any, List +from pydantic import Field + from ...core.main import ChatMessage from . import LLM, CompletionOptions class QueuedLLM(LLM): - llm: LLM + """ + QueuedLLM exists to make up for LLM servers that cannot handle multiple requests at once. It uses a lock to ensure that only one request is being processed at a time. + + If you are already using another LLM class and are experiencing this problem, you can just wrap it with the QueuedLLM class like this: + + ```python + from continuedev.src.continuedev.libs.llm.queued import QueuedLLM + + config = ContinueConfig( + ... + models=Models( + default=QueuedLLM(llm=<OTHER_LLM_CLASS>) + ) + ) + ``` + """ + + llm: LLM = Field(..., description="The LLM to wrap with a lock") _lock: asyncio.Lock model: str = "queued" diff --git a/continuedev/src/continuedev/libs/llm/replicate.py b/continuedev/src/continuedev/libs/llm/replicate.py index 1ed493c1..02d9bfd7 100644 --- a/continuedev/src/continuedev/libs/llm/replicate.py +++ b/continuedev/src/continuedev/libs/llm/replicate.py @@ -2,6 +2,7 @@ import concurrent.futures from typing import List import replicate +from pydantic import Field from ...core.main import ChatMessage from . import LLM @@ -9,8 +10,27 @@ from .prompts.edit import simplified_edit_prompt class ReplicateLLM(LLM): - api_key: str - "Replicate API key" + """ + Replicate is a great option for newly released language models or models that you've deployed through their platform. Sign up for an account [here](https://replicate.ai/), copy your API key, and then select any model from the [Replicate Streaming List](https://replicate.com/collections/streaming-language-models). Change `~/.continue/config.py` to look like this: + + ```python + from continuedev.src.continuedev.core.models import Models + from continuedev.src.continuedev.libs.llm.replicate import ReplicateLLM + + config = ContinueConfig( + ... + models=Models( + default=ReplicateLLM( + model="replicate/codellama-13b-instruct:da5676342de1a5a335b848383af297f592b816b950a43d251a0a9edd0113604b", + api_key="my-replicate-api-key") + ) + ) + ``` + + If you don't specify the `model` parameter, it will default to `replicate/llama-2-70b-chat:58d078176e02c219e11eb4da5a02a7830a283b14cf8f94537af893ccff5ee781`. + """ + + api_key: str = Field(..., description="Replicate API key") model: str = "replicate/llama-2-70b-chat:58d078176e02c219e11eb4da5a02a7830a283b14cf8f94537af893ccff5ee781" diff --git a/continuedev/src/continuedev/libs/llm/text_gen_interface.py b/continuedev/src/continuedev/libs/llm/text_gen_interface.py index e37366c7..3c76a21a 100644 --- a/continuedev/src/continuedev/libs/llm/text_gen_interface.py +++ b/continuedev/src/continuedev/libs/llm/text_gen_interface.py @@ -2,6 +2,7 @@ import json from typing import Any, List, Optional import websockets +from pydantic import Field from ...core.main import ChatMessage from . import LLM @@ -9,10 +10,34 @@ from .prompts.edit import simplified_edit_prompt class TextGenUI(LLM): + """ + TextGenUI is a comprehensive, open-source language model UI and local server. You can set it up with an OpenAI-compatible server plugin, but if for some reason that doesn't work, you can use this class like so: + + ```python + from continuedev.src.continuedev.libs.llm.text_gen_interface import TextGenUI + + config = ContinueConfig( + ... + models=Models( + default=TextGenUI( + model="<MODEL_NAME>", + ) + ) + ) + ``` + """ + model: str = "text-gen-ui" - server_url: str = "http://localhost:5000" - streaming_url: str = "http://localhost:5005" - verify_ssl: Optional[bool] = None + server_url: str = Field( + "http://localhost:5000", description="URL of your TextGenUI server" + ) + streaming_url: str = Field( + "http://localhost:5005", + description="URL of your TextGenUI streaming server (separate from main server URL)", + ) + verify_ssl: Optional[bool] = Field( + None, description="Whether to verify SSL certificates for requests." + ) prompt_templates = { "edit": simplified_edit_prompt, diff --git a/continuedev/src/continuedev/libs/llm/together.py b/continuedev/src/continuedev/libs/llm/together.py index a9db70c6..a381abab 100644 --- a/continuedev/src/continuedev/libs/llm/together.py +++ b/continuedev/src/continuedev/libs/llm/together.py @@ -2,6 +2,7 @@ import json from typing import Callable, Optional import aiohttp +from pydantic import Field from ...core.main import ContinueCustomException from ..llm import LLM @@ -11,12 +12,36 @@ from .prompts.edit import simplified_edit_prompt class TogetherLLM(LLM): - api_key: str - "Together API key" + """ + The Together API is a cloud platform for running large AI models. You can sign up [here](https://api.together.xyz/signup), copy your API key on the initial welcome screen, and then hit the play button on any model from the [Together Models list](https://docs.together.ai/docs/models-inference). Change `~/.continue/config.py` to look like this: + + ```python + from continuedev.src.continuedev.core.models import Models + from continuedev.src.continuedev.libs.llm.together import TogetherLLM + + config = ContinueConfig( + ... + models=Models( + default=TogetherLLM( + api_key="<API_KEY>", + model="togethercomputer/llama-2-13b-chat" + ) + ) + ) + ``` + """ + + api_key: str = Field(..., description="Together API key") model: str = "togethercomputer/RedPajama-INCITE-7B-Instruct" - base_url: str = "https://api.together.xyz" - verify_ssl: Optional[bool] = None + base_url: str = Field( + "https://api.together.xyz", + description="The base URL for your Together API instance", + ) + verify_ssl: Optional[bool] = Field( + None, + description="Whether SSL certificates should be verified when making the HTTP request", + ) _client_session: aiohttp.ClientSession = None diff --git a/continuedev/src/continuedev/models/reference/test.py b/continuedev/src/continuedev/models/reference/test.py index 2d1db3e1..58cfc3fe 100644 --- a/continuedev/src/continuedev/models/reference/test.py +++ b/continuedev/src/continuedev/models/reference/test.py @@ -1,6 +1,7 @@ +import html import importlib import json -from textwrap import dedent # noqa: F401 +from textwrap import dedent LLM_MODULES = [ ("openai", "OpenAI"), @@ -9,9 +10,23 @@ LLM_MODULES = [ ("llamacpp", "LlamaCpp"), ("text_gen_interface", "TextGenUI"), ("ollama", "Ollama"), - ("queued", "QueuedLLM"), ("replicate", "ReplicateLLM"), ("together", "TogetherLLM"), + ("hf_inference_api", "HuggingFaceInferenceAPI"), + ("hf_tgi", "HuggingFaceTGI"), + ("maybe_proxy_openai", "MaybeProxyOpenAI"), + ("queued", "QueuedLLM"), +] + +CONTEXT_PROVIDER_MODULES = [ + ("diff", "DiffContextProvider"), + ("file", "FileContextProvider"), + ("filetree", "FileTreeContextProvider"), + ("github", "GitHubIssuesContextProvider"), + ("google", "GoogleContextProvider"), + ("search", "SearchContextProvider"), + ("terminal", "TerminalContextProvider"), + ("url", "URLContextProvider"), ] @@ -22,9 +37,40 @@ def import_llm_module(module_name, module_title): return obj -def llm_docs_from_schema(schema, filename): +def import_context_provider_module(module_name, module_title): + module_name = f"continuedev.src.continuedev.plugins.context_providers.{module_name}" + module = importlib.import_module(module_name) + obj = getattr(module, module_title) + return obj + + +def docs_from_schema(schema, filename, ignore_properties=[], inherited=[]): # Generate markdown docs - markdown_docs = dedent( + properties = "" + inherited_properties = "" + + def add_property(prop, details, only_required): + required = prop in schema.get("required", []) + if only_required != required or prop in ignore_properties: + return "" + required = "true" if required else "false" + return f"""<ClassPropertyRef name='{prop}' details='{html.escape(json.dumps(details))}' required={{{required}}} default="{html.escape(str(details.get("default", "")))}"/>""" + + for prop, details in schema["properties"].items(): + property = add_property(prop, details, True) + if prop in inherited: + inherited_properties += property + else: + properties += property + + for prop, details in schema["properties"].items(): + property = add_property(prop, details, False) + if prop in inherited: + inherited_properties += property + else: + properties += property + + return dedent( f"""\ import ClassPropertyRef from '@site/src/components/ClassPropertyRef.tsx'; @@ -36,29 +82,53 @@ import ClassPropertyRef from '@site/src/components/ClassPropertyRef.tsx'; ## Properties -""" - ) +{properties} - for prop, details in schema["properties"].items(): - required = prop in schema.get("required", []) - if not required: - continue - required = "true" if required else "false" - markdown_docs += f"<ClassPropertyRef name='{prop}' details='{json.dumps(details)}' required={{{required}}}/>" +### Inherited Properties - for prop, details in schema["properties"].items(): - required = prop in schema.get("required", []) - if required: - continue - required = "true" if required else "false" - markdown_docs += f"<ClassPropertyRef name='{prop}' details='{json.dumps(details)}' required={{{required}}}/>" +{inherited_properties}""" + ) - return markdown_docs +llm_module = importlib.import_module("continuedev.src.continuedev.libs.llm") +llm_obj = getattr(llm_module, "LLM") +schema = llm_obj.schema() +llm_properties = schema["properties"].keys() for module_name, module_title in LLM_MODULES: obj = import_llm_module(module_name, module_title) schema = obj.schema() - markdown_docs = llm_docs_from_schema(schema, module_name) + markdown_docs = docs_from_schema(schema, module_name, inherited=llm_properties) with open(f"docs/docs/reference/Models/{module_name}.md", "w") as f: f.write(markdown_docs) + +config_module = importlib.import_module("continuedev.src.continuedev.core.config") +config_obj = getattr(config_module, "ContinueConfig") +schema = config_obj.schema() +markdown_docs = docs_from_schema(schema, "config") +with open("docs/docs/reference/config.md", "w") as f: + f.write(markdown_docs) + +for module_name, module_title in CONTEXT_PROVIDER_MODULES: + obj = import_context_provider_module(module_name, module_title) + schema = obj.schema() + markdown_docs = docs_from_schema( + schema, + module_name, + ignore_properties=[ + "sdk", + "updated_documents", + "delete_documents", + "selected_items", + "ignore_patterns", + ], + ) + with open(f"docs/docs/reference/Context Providers/{module_name}.md", "w") as f: + f.write(markdown_docs) + +# sdk_module = importlib.import_module("continuedev.src.continuedev.core.sdk") +# sdk_obj = getattr(sdk_module, "ContinueSDK") +# schema = sdk_obj.schema() +# markdown_docs = docs_from_schema(schema, "sdk", ignore_properties=[]) +# with open("docs/docs/reference/ContinueSDK.md", "w") as f: +# f.write(markdown_docs) |