From 49a893549296067c7261e14369fc28b7ebbc7aad Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Wed, 1 Nov 2023 12:03:26 +0200 Subject: [PATCH 01/20] add docstring to tokenizer singelthon --- src/canopy/tokenizer/tokenizer.py | 108 +++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index 7e9d53f6..508c23c9 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -1,10 +1,29 @@ -from typing import List, Optional +from typing import List, Optional, Type from .openai import OpenAITokenizer from .base import BaseTokenizer class Tokenizer: + + """ + Singleton class for tokenization. + The singleton behavior unify tokenization across the system. + + Usage: + + To initialize the tokenizer, call Tokenizer.initialize(tokenizer_class, *args, **kwargs) + >>> from canopy.tokenizer import Tokenizer + >>> Tokenizer.initialize() + + Then, you can init a tokenizer instance by calling Tokenizer() from anywhere in the code and use it: + >>> tokenizer = Tokenizer() + >>> tokenizer.tokenize("Hello world!") + ['Hello', 'world', '!'] + >>> tokenizer.detokenize(['Hello', 'world', '!']) + 'Hello world!' + """ # noqa: E501 + _instance = None _tokenizer_instance: Optional[BaseTokenizer] = None _initialized = False @@ -20,7 +39,43 @@ def __new__(cls): return cls._instance @classmethod - def initialize(cls, tokenizer_class=DEFAULT_TOKENIZER_CLASS, **kwargs): + def initialize(cls, + tokenizer_class: Type[BaseTokenizer] = DEFAULT_TOKENIZER_CLASS, + **kwargs): + """ + Initialize the tokenizer singleton. + + Args: + tokenizer_class: The tokenizer class to use. Must be a subclass of BaseTokenizer. Defaults to OpenAITokenizer. + **kwargs: Keyword arguments to pass to the tokenizer class constructor. + + Returns: + None + + Examples: + Initialize the tokenizer with the default tokenizer class: + + >>> from canopy.tokenizer import Tokenizer + >>> Tokenizer.initialize() + + Initialize the tokenizer with a custom tokenizer class: + + >>> from canopy.tokenizer import Tokenizer + >>> from canopy.tokenizer.base import BaseTokenizer + >>> class MyTokenizer(BaseTokenizer): + ... def tokenize(self, text: str) -> List[str]: + ... return text.split() + ... def detokenize(self, tokens: List[str]) -> str: + ... return " ".join(tokens) + ... def messages_token_count(self, messages) -> int: + ... return sum([self.token_count(message) + 3 for message in messages]) + >>> Tokenizer.initialize(MyTokenizer) + + Then, you can init a tokenizer instance by calling Tokenizer() from anywhere in the code: + + >>> from canopy.tokenizer import Tokenizer + >>> tokenizer = Tokenizer() + """ # noqa: E501 if not issubclass(tokenizer_class, BaseTokenizer): raise ValueError("Invalid tokenizer class provided") if issubclass(tokenizer_class, Tokenizer): @@ -30,12 +85,22 @@ def initialize(cls, tokenizer_class=DEFAULT_TOKENIZER_CLASS, **kwargs): @classmethod def clear(cls): + """ + Clear the tokenizer singleton. + """ cls._instance = None cls._tokenizer_instance = None cls._initialized = False @classmethod def initialize_from_config(cls, config: dict): + """ + Initialize the tokenizer singleton from a config dictionary. + Used by the config module to initialize the tokenizer from a config file. + + Args: + config: A dictionary containing the tokenizer configuration. Must contain a "type" key with the tokenizer class name. + """ # noqa: E501 if cls._initialized: raise ValueError("Tokenizer has already been initialized") config["type"] = config.get("type", cls.DEFAULT_TOKENIZER_CLASS.__name__) @@ -43,13 +108,52 @@ def initialize_from_config(cls, config: dict): cls._initialized = True def tokenize(self, text: str) -> List[str]: + """ + Splits a text into tokens. + + Args: + text: The text to tokenize as a string. + + Returns: + A list of tokens. + """ return self._tokenizer_instance.tokenize(text) # type: ignore[union-attr] def detokenize(self, tokens: List[str]) -> str: + """ + Joins a list of tokens into a text. + + Args: + tokens: The tokens to join as a list of strings. Consider using tokenize() first. + + Returns: + The joined text as a string. + """ return self._tokenizer_instance.detokenize(tokens) # type: ignore[union-attr] def token_count(self, text: str) -> int: + """ + Counts the number of tokens in a text. + + Args: + text: The text to count as a string. + + Returns: + The number of tokens in the text. + """ return self._tokenizer_instance.token_count(text) # type: ignore[union-attr] def messages_token_count(self, messages) -> int: + """ + Counts the number of tokens in a Messages object. + Behind the scenes, for each LLM provider there might be a different overhead for each message in the prompt, + which is not necessarily the same as the number of tokens in the message text. + This method takes care of that overhead and returns the total number of tokens in the prompt, as counted by the LLM provider. + + Args: + messages: The Messages object to count. + + Returns: + The number of tokens in the Messages object. + """ # noqa: E501 return self._tokenizer_instance.messages_token_count(messages) # type: ignore[union-attr] # noqa: E501 From 791abd81ce1c07e713a03423f3c95d37495ae9df Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Wed, 1 Nov 2023 12:13:18 +0200 Subject: [PATCH 02/20] add docstrings to openai tokenizer --- src/canopy/tokenizer/openai.py | 59 +++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/canopy/tokenizer/openai.py b/src/canopy/tokenizer/openai.py index 069925d9..180cd4fd 100644 --- a/src/canopy/tokenizer/openai.py +++ b/src/canopy/tokenizer/openai.py @@ -5,30 +5,87 @@ class OpenAITokenizer(BaseTokenizer): + """ + Tokenizer for OpenAI models, based on the tiktoken library. + + Usage: + Initialize the singleton tokenizer with the OpenAITokenizer class: + >>> from canopy.tokenizer import Tokenizer + >>> Tokenizer.initialize(tokenizer_class=OpenAITokenizer, model_name="gpt-3.5-turbo") + + You can then use the tokenizer instance from anywhere in the code: + >>> from canopy.tokenizer import Tokenizer + >>> tokenizer = Tokenizer() + >>> tokenizer.tokenize("Hello world!") + ['Hello', ' world', '!'] + """ # noqa: E501 MESSAGE_TOKENS_OVERHEAD = 3 FIXED_PREFIX_TOKENS = 3 def __init__(self, model_name: str = "gpt-3.5-turbo"): + """ + Args: + model_name: The name of the model to use. Defaults to "gpt-3.5-turbo". + You can find the list of available models here: https://github.com/openai/tiktoken/blob/39f29cecdb6fc38d9a3434e5dd15e4de58cf3c80/tiktoken/model.py#L19C1-L19C18 + As you can see, both gpt-3.5 and gpt-4 are using the same cl100k_base tokenizer. + """ # noqa: E501 self._encoder = tiktoken.encoding_for_model(model_name) def tokenize(self, text: str) -> List[str]: + """ + Tokenize a text using tiktoken. + + Args: + text: The text to tokenize. + + Returns: + The list of tokens. + """ return [self._encoder.decode([encoded_token]) for encoded_token in self._encode(text)] def detokenize(self, tokens: List[str]) -> str: + """ + Detokenize a list of tokens that were previously tokenized using this tokenizer. + + Args: + tokens: The list of tokens to detokenize. + + Returns: + The detokenized text as a string. + """ if not isinstance(tokens, List): raise TypeError(f"detokenize expect List[str], got f{type(tokens)}") return "".join(tokens) def token_count(self, text: str) -> int: + """ + Count the number of tokens in a text. + + Args: + text: The text to count the tokens of. + + Returns: + The number of tokens in the text. + """ return len(self._encode(text)) def _encode(self, text): return self._encoder.encode(text, disallowed_special=()) def messages_token_count(self, messages: Messages) -> int: - # Adapted from: https://github.com/openai/openai-cookbook/.../How_to_format_inputs_to_ChatGPT_models.ipynb # noqa + """ + Count the number of tokens in a list of messages as expected to be counted by OpenAI models. + Account for the overhead of the messages structure. + Taken from: https://github.com/openai/openai-cookbook/.../How_to_format_inputs_to_ChatGPT_models.ipynb + + Args: + messages: The list of messages to count the tokens of. + + Returns: + The number of tokens in the messages, as expected to be counted by OpenAI models. + """ # noqa: E501 num_tokens = 0 for message in messages: num_tokens += self.MESSAGE_TOKENS_OVERHEAD From c0ed6d3591af66f5a3297eb582dfdad082cdb3de Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Wed, 1 Nov 2023 12:17:31 +0200 Subject: [PATCH 03/20] lint --- src/canopy/tokenizer/openai.py | 10 ++++++---- src/canopy/tokenizer/tokenizer.py | 20 +++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/canopy/tokenizer/openai.py b/src/canopy/tokenizer/openai.py index 180cd4fd..2c00256a 100644 --- a/src/canopy/tokenizer/openai.py +++ b/src/canopy/tokenizer/openai.py @@ -7,12 +7,12 @@ class OpenAITokenizer(BaseTokenizer): """ Tokenizer for OpenAI models, based on the tiktoken library. - + Usage: Initialize the singleton tokenizer with the OpenAITokenizer class: >>> from canopy.tokenizer import Tokenizer >>> Tokenizer.initialize(tokenizer_class=OpenAITokenizer, model_name="gpt-3.5-turbo") - + You can then use the tokenizer instance from anywhere in the code: >>> from canopy.tokenizer import Tokenizer >>> tokenizer = Tokenizer() @@ -25,6 +25,8 @@ class OpenAITokenizer(BaseTokenizer): def __init__(self, model_name: str = "gpt-3.5-turbo"): """ + Initialize the tokenizer. + Args: model_name: The name of the model to use. Defaults to "gpt-3.5-turbo". You can find the list of available models here: https://github.com/openai/tiktoken/blob/39f29cecdb6fc38d9a3434e5dd15e4de58cf3c80/tiktoken/model.py#L19C1-L19C18 @@ -79,10 +81,10 @@ def messages_token_count(self, messages: Messages) -> int: Count the number of tokens in a list of messages as expected to be counted by OpenAI models. Account for the overhead of the messages structure. Taken from: https://github.com/openai/openai-cookbook/.../How_to_format_inputs_to_ChatGPT_models.ipynb - + Args: messages: The list of messages to count the tokens of. - + Returns: The number of tokens in the messages, as expected to be counted by OpenAI models. """ # noqa: E501 diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index 508c23c9..4a5d08f5 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -2,6 +2,7 @@ from .openai import OpenAITokenizer from .base import BaseTokenizer +from ..models.data_models import Messages class Tokenizer: @@ -9,13 +10,13 @@ class Tokenizer: """ Singleton class for tokenization. The singleton behavior unify tokenization across the system. - + Usage: - + To initialize the tokenizer, call Tokenizer.initialize(tokenizer_class, *args, **kwargs) >>> from canopy.tokenizer import Tokenizer >>> Tokenizer.initialize() - + Then, you can init a tokenizer instance by calling Tokenizer() from anywhere in the code and use it: >>> tokenizer = Tokenizer() >>> tokenizer.tokenize("Hello world!") @@ -49,9 +50,6 @@ def initialize(cls, tokenizer_class: The tokenizer class to use. Must be a subclass of BaseTokenizer. Defaults to OpenAITokenizer. **kwargs: Keyword arguments to pass to the tokenizer class constructor. - Returns: - None - Examples: Initialize the tokenizer with the default tokenizer class: @@ -97,7 +95,7 @@ def initialize_from_config(cls, config: dict): """ Initialize the tokenizer singleton from a config dictionary. Used by the config module to initialize the tokenizer from a config file. - + Args: config: A dictionary containing the tokenizer configuration. Must contain a "type" key with the tokenizer class name. """ # noqa: E501 @@ -128,7 +126,7 @@ def detokenize(self, tokens: List[str]) -> str: Returns: The joined text as a string. - """ + """ # noqa: E501 return self._tokenizer_instance.detokenize(tokens) # type: ignore[union-attr] def token_count(self, text: str) -> int: @@ -143,16 +141,16 @@ def token_count(self, text: str) -> int: """ return self._tokenizer_instance.token_count(text) # type: ignore[union-attr] - def messages_token_count(self, messages) -> int: + def messages_token_count(self, messages: Messages) -> int: """ Counts the number of tokens in a Messages object. Behind the scenes, for each LLM provider there might be a different overhead for each message in the prompt, which is not necessarily the same as the number of tokens in the message text. This method takes care of that overhead and returns the total number of tokens in the prompt, as counted by the LLM provider. - + Args: messages: The Messages object to count. - + Returns: The number of tokens in the Messages object. """ # noqa: E501 From 4e9321be52193a8a6014c9fb6deef78c09e81474 Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Wed, 1 Nov 2023 15:59:02 +0200 Subject: [PATCH 04/20] llm docstring --- src/canopy/llm/openai.py | 72 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/canopy/llm/openai.py b/src/canopy/llm/openai.py index 4cb3d43f..90f212a5 100644 --- a/src/canopy/llm/openai.py +++ b/src/canopy/llm/openai.py @@ -16,7 +16,15 @@ class OpenAILLM(BaseLLM): - + """ + OpenAI LLM wrapper built on top of the OpenAI Python client. + + Note: OpenAI requires a valid API key to use this class. + You can set the "OPENAI_API_KEY" environment variable to your API key. + Or you can directly set it as follows: + >>> import openai + >>> openai.api_key = "YOUR_API_KEY" + """ def __init__(self, model_name: str = "gpt-3.5-turbo", *, @@ -42,6 +50,29 @@ def chat_completion(self, max_tokens: Optional[int] = None, model_params: Optional[ModelParams] = None, ) -> Union[ChatResponse, Iterable[StreamingChatChunk]]: + """ + Chat completion using the OpenAI API. + + Note: this function is wrapped in a retry decorator to handle transient errors. + + Args: + messages: Messages (chat history) to send to the model. + stream: Whether to stream the response or not. + max_tokens: Maximum number of tokens to generate. Defaults to None (generates until stop sequence or until hitting max context size). + model_params: Model parameters to use for this request. Defaults to None (uses the default model parameters). + see: https://platform.openai.com/docs/api-reference/chat/create + Returns: + ChatResponse or StreamingChatChunk + + Usage: + >>> from canopy.llm import OpenAILLM + >>> from canopy.models.data_models import UserMessage + >>> llm = OpenAILLM() + >>> messages = [UserMessage(content="Hello! How are you?")] + >>> result = llm.chat_completion(messages) + >>> print(result.choices[0].message.content) + "I'm good, how are you?" + """ # noqa: E501 model_params_dict: Dict[str, Any] = {} model_params_dict.update( @@ -80,6 +111,45 @@ def enforced_function_call(self, *, max_tokens: Optional[int] = None, model_params: Optional[ModelParams] = None) -> dict: + """ + This function enforces the model to response with a specific function call. + + To read more about this feature, see: https://platform.openai.com/docs/guides/gpt/function-calling + + Note: this function is wrapped in a retry decorator to handle transient errors. + + Args: + messages: Messages (chat history) to send to the model. + function: Function to call. See canopy.llm.models.Function for more details. + max_tokens: Maximum number of tokens to generate. Defaults to None (generates until stop sequence or until hitting max context size). + model_params: Model parameters to use for this request. Defaults to None (uses the default model parameters). + see: https://platform.openai.com/docs/api-reference/chat/create + + Returns: + dict: Function call arguments as a dictionary. + + Usage: + >>> from canopy.llm import OpenAILLM + >>> from canopy.llm.models import Function, FunctionParameters, FunctionArrayProperty + >>> from canopy.models.data_models import UserMessage + >>> llm = OpenAILLM() + >>> messages = [UserMessage(content="I was wondering what is the capital of France?")] + >>> function = Function( + ... name="query_knowledgebase", + ... description="Query search engine for relevant information", + ... parameters=FunctionParameters( + ... required_properties=[ + ... FunctionArrayProperty( + ... name="queries", + ... items_type="string", + ... description='List of queries to send to the search engine.', + ... ), + ... ] + ... ) + ... ) + >>> llm.enforced_function_call(messages, function) + {'queries': ['capital of France']} + """ # this enforces the model to call the function function_call = {"name": function.name} From 25c5d362d43ccc4a60434a953b2cb98cc44f633b Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Wed, 1 Nov 2023 16:10:08 +0200 Subject: [PATCH 05/20] lint --- src/canopy/llm/openai.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/canopy/llm/openai.py b/src/canopy/llm/openai.py index 90f212a5..e5ffbfd6 100644 --- a/src/canopy/llm/openai.py +++ b/src/canopy/llm/openai.py @@ -52,7 +52,7 @@ def chat_completion(self, ) -> Union[ChatResponse, Iterable[StreamingChatChunk]]: """ Chat completion using the OpenAI API. - + Note: this function is wrapped in a retry decorator to handle transient errors. Args: @@ -63,7 +63,7 @@ def chat_completion(self, see: https://platform.openai.com/docs/api-reference/chat/create Returns: ChatResponse or StreamingChatChunk - + Usage: >>> from canopy.llm import OpenAILLM >>> from canopy.models.data_models import UserMessage @@ -149,7 +149,7 @@ def enforced_function_call(self, ... ) >>> llm.enforced_function_call(messages, function) {'queries': ['capital of France']} - """ + """ # noqa: E501 # this enforces the model to call the function function_call = {"name": function.name} From 2c362758213ae7b9e041991ebf85a3f345da7086 Mon Sep 17 00:00:00 2001 From: ilai Date: Wed, 1 Nov 2023 19:25:07 +0200 Subject: [PATCH 06/20] [Readme] Improve library section There were some inaccuracies there --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01e650e7..be17c012 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,10 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and ## What's inside the box? -1. **Canopy Core Library** - Canopy has 3 API level components that are responsible for different parts of the RAG workflow: - * **ChatEngine** _`/chat/completions`_ - implements the full RAG workflow and exposes a chat interface to interact with your data. It acts as a wrapper around the Knowledge Base and Context Engine. - * **ContextEngine** - performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to be used as a prompt for the LLM. +1. **Canopy Core Library** - Canopy includes 3 main components that are responsible for different parts of the RAG workflow: + * **KnowledgeBase** - Manages your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings, storing them in a Pinecone vector database. Given a new textual query - the `KnowledgeBase` will retrieve the most relevant document chunks from the database. + * **ContextEngine** - Performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to be used as a prompt for the LLM. + * **ChatEngine** - Exposes a chat interface to interact with your data. Given chat messages history, the `ChatEngine` uses the `ContextEngine` to generate a prompt and send it to an underlying LLM, returning a knowledge-augmented response. * **KnowledgeBase** _`/context/{upsert, delete}` - prepares your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings before upserting them into the Pinecone vector database. It also handles Delete operations. From 3727deae04e05873bb7d72d9b2db2f937a150572 Mon Sep 17 00:00:00 2001 From: ilai Date: Wed, 1 Nov 2023 19:25:39 +0200 Subject: [PATCH 07/20] [Readme] Rename service to server This term makes more sense... --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index be17c012..bf5b5027 100644 --- a/README.md +++ b/README.md @@ -53,14 +53,14 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and * **ContextEngine** - Performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to be used as a prompt for the LLM. * **ChatEngine** - Exposes a chat interface to interact with your data. Given chat messages history, the `ChatEngine` uses the `ContextEngine` to generate a prompt and send it to an underlying LLM, returning a knowledge-augmented response. - * **KnowledgeBase** _`/context/{upsert, delete}` - prepares your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings before upserting them into the Pinecone vector database. It also handles Delete operations. > more information about the Core Library usage can be found in the [Library Documentation](docs/library.md) -2. **Canopy Service** - a webservice that wraps the **Canopy Core** and exposes it as a REST API. The service is built on top of FastAPI, Uvicorn and Gunicorn and can be easily deployed in production. The service also comes with a built in Swagger UI for easy testing and documentation. After you [start the server](#3-start-the-canopy-service), you can access the Swagger UI at `http://host:port/docs` (default: `http://localhost:8000/docs`) +2. **Canopy Server** - a webservice that wraps the **Canopy Core** and exposes it as a REST API. The server is built on top of FastAPI, Uvicorn and Gunicorn and can be easily deployed in production. +3. The server also comes with a built-in Swagger UI for easy testing and documentation. After you [start the server](#3-start-the-canopy-server), you can access the Swagger UI at `http://host:port/docs` (default: `http://localhost:8000/docs`) 3. **Canopy CLI** - A built-in development tool that allows users to swiftly set up their own Canopy server and test its configuration. -With just three CLI commands, you can create a new Canopy service, upload your documents to it, and then interact with the Chatbot using a built-in chat application directly from the terminal. The built-in chatbot also enables comparison of RAG-infused responses against a native LLM chatbot. +With just three CLI commands, you can create a new Canopy server, upload your documents to it, and then interact with the Chatbot using a built-in chat application directly from the terminal. The built-in chatbot also enables comparison of RAG-infused responses against a native LLM chatbot. ## Considerations @@ -102,7 +102,7 @@ export INDEX_NAME= | `PINECONE_ENVIRONMENT`| Determines the Pinecone service cloud environment of your index e.g `west1-gcp`, `us-east-1-aws`, etc | You can find the Pinecone environment next to the API key in [console](https://app.pinecone.io/) | | `OPENAI_API_KEY` | API key for OpenAI. Used to authenticate to OpenAI's services for embedding and chat API | You can find your OpenAI API key [here](https://platform.openai.com/account/api-keys). You might need to login or register to OpenAI services | | `INDEX_NAME` | Name of the Pinecone index Canopy will underlying work with | You can choose any name as long as it follows Pinecone's [restrictions](https://support.pinecone.io/hc/en-us/articles/11729246212637-Are-there-restrictions-on-index-names-#:~:text=There%20are%20two%20main%20restrictions,and%20emojis%20are%20not%20supported.) | -| `CANOPY_CONFIG_FILE` | The path of a configuration yaml file to be used by the Canopy service. | Optional - if not provided, default configuration would be used | +| `CANOPY_CONFIG_FILE` | The path of a configuration yaml file to be used by the Canopy server. | Optional - if not provided, default configuration would be used | @@ -163,9 +163,10 @@ Canopy support single or mulitple files in jsonl or praquet format. The document Follow the instructions in the CLI to upload your data. -### 3. Start the **Canopy** service +### 3. Start the **Canopy** server -**Canopy** service serve as a proxy between your application and Pinecone. It will also handle the RAG part of the application. To start the service, run: +**Canopy** The canopy server exposes Canopy's functionality via a REST API. Namely, it allows you to upload documents, retrieve relevant docs for a given query, and chat with your data. The server exposes a `/chat.completion` endpoint that can be easily integrated with any chat application. +To start the server, run: ```bash canopy start @@ -182,7 +183,7 @@ INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) > **_📝 NOTE:_** > > The canopy start command will keep the terminal occupied. To proceed with the next steps, please open a new terminal window. -> If you want to run the service in the background, you can use the following command - **```nohup canopy start &```** +> If you want to run the server in the background, you can use the following command - **```nohup canopy start &```** > However, this is not recommended. @@ -204,11 +205,11 @@ canopy chat --baseline This will open a similar chat interface window, but will send your question directly to the LLM without the RAG pipeline. -### 5. Stop the **Canopy** service +### 5. Stop the **Canopy** server -To stop the service, simply press `CTRL+C` in the terminal where you started it. +To stop the server, simply press `CTRL+C` in the terminal where you started it. -If you have started the service in the background, you can stop it by running: +If you have started the server in the background, you can stop it by running: ```bash canopy stop @@ -223,7 +224,7 @@ If you already have an application that uses the OpenAI API, you can migrate it ```python import openai -openai.api_base = "http://host:port/context" +openai.api_base = "http://host:port/" # now you can use the OpenAI API as usual ``` @@ -233,14 +234,14 @@ or without global state change: ```python import openai -openai_response = openai.Completion.create(..., api_base="http://host:port/context") +openai_response = openai.Completion.create(..., api_base="http://host:port/") ``` -### Running Canopy service in production +### Running Canopy server in production Canopy is using FastAPI as the web framework and Uvicorn as the ASGI server. It is recommended to use Gunicorn as the production server, mainly because it supports multiple worker processes and can handle multiple requests in parallel, more details can be found [here](https://www.uvicorn.org/deployment/#using-a-process-manager). -To run the canopy service for production, please run: +To run the canopy server for production, please run: ```bash gunicorn canopy_cli.app:app --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers From e05d13d0fe986a35d437b7f87ec923cefb8871bd Mon Sep 17 00:00:00 2001 From: ilai Date: Wed, 1 Nov 2023 19:26:26 +0200 Subject: [PATCH 08/20] [Readme] Added comment about security \ authentication Better be safe than sorry --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bf5b5027..365a3653 100644 --- a/README.md +++ b/README.md @@ -246,3 +246,5 @@ To run the canopy server for production, please run: ```bash gunicorn canopy_cli.app:app --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers ``` + +The server interacts with services like Pinecone and OpenAI using your own authentication credentials. When deploying the server on a public web hosting provider, it is recommended to enable an authentication mechanism, so that your server would only take requests from authenticated users. \ No newline at end of file From 32d5a3d6fc743a7361fe8334391a889710bc276d Mon Sep 17 00:00:00 2001 From: ilai Date: Wed, 1 Nov 2023 19:45:47 +0200 Subject: [PATCH 09/20] [readme] Separated the CLI chat from quickstart --- README.md | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 365a3653..ce14c6fb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Canopy is desinged to be: * **Easy to implement:** Bring your text data in Parquet or JSONL format, and Canopy will handle the rest. Canopy makes it easy to incorporate RAG into your OpenAI chat applications. * **Reliable at scale:** Build fast, highly accurate GenAI applications that are production-ready and backed by Pinecone’s vector database. Seamlessly scale to billions of items with transarent, resource-based pricing. * **Open and flexible:** Fully open-source, Canopy is both modular and extensible. You can configure to choose the components you need, or extend any component with your own custom implementation. Easily incorporate it into existing OpenAI applications and connect Canopy to your preferred UI. -* **Interactive and iterative:** Evaluate your RAG workflow with a CLI based chat tool. With a simple command in the Canopy CLI you can interactively chat with your text data and compare RAG vs. non-RAG workflows side-by-side to evaluate the augmented results before scaling to production. +* **Interactive and iterative:** Evaluate your RAG workflow with a CLI based chat tool. With a simple command in the Canopy CLI you can interactively chat with your text data and compare RAG vs. non-RAG workflows side-by-side to evaluate the results before scaling to production. ## RAG with Canopy @@ -126,19 +126,20 @@ In this quickstart, we will show you how to use the **Canopy** to build a simple ### 1. Create a new **Canopy** Index -**Canopy** will create and configure a new Pinecone index on your behalf. Just run: +As a one-time setup, Canopy needs to create and configure a new Pinecone index configured to work with Canopy. Just run: ```bash canopy new ``` -And follow the CLI instructions. The index that will be created will have a prefix `canopy--`. This will have to be done only once per index. +And follow the CLI instructions. The index that will be created will have a prefix `canopy--`. +You only have to do this process once for every Canopy index you want to create. > To learn more about Pinecone Indexes and how to manage them, please refer to the following guide: [Understanding indexes](https://docs.pinecone.io/docs/indexes) ### 2. Uploading data -You can load data into your **Canopy** Index by simply using the CLI: +You can load data into your Canopy Index using the command: ```bash canopy upsert /path/to/data_directory @@ -150,7 +151,7 @@ canopy upsert /path/to/data_directory/file.parquet canopy upsert /path/to/data_directory/file.jsonl ``` -Canopy support single or mulitple files in jsonl or praquet format. The documents should have the following schema: +Canopy supports files in `jsonl` or `parquet` format. The documents should have the following schema: ``` +----------+--------------+--------------+---------------+ @@ -163,58 +164,61 @@ Canopy support single or mulitple files in jsonl or praquet format. The document Follow the instructions in the CLI to upload your data. -### 3. Start the **Canopy** server +### 3. Start the Canopy server -**Canopy** The canopy server exposes Canopy's functionality via a REST API. Namely, it allows you to upload documents, retrieve relevant docs for a given query, and chat with your data. The server exposes a `/chat.completion` endpoint that can be easily integrated with any chat application. +The canopy server exposes Canopy's functionality via a REST API. Namely, it allows you to upload documents, retrieve relevant docs for a given query, and chat with your data. The server exposes a `/chat.completion` endpoint that can be easily integrated with any chat application. To start the server, run: ```bash canopy start ``` - Now, you should be prompted with the following standard Uvicorn message: - ``` ... INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) ``` +**That's it!** you can now start using the **Canopy** server with any chat application that supports a `/chat.completion` endpoint. -> **_📝 NOTE:_** +> _📝 NOTE:_ > -> The canopy start command will keep the terminal occupied. To proceed with the next steps, please open a new terminal window. +> The canopy start command will keep the terminal occupied. > If you want to run the server in the background, you can use the following command - **```nohup canopy start &```** > However, this is not recommended. -### 4. Chat with your data +### Stopping the server +To stop the server, simply press `CTRL+C` in the terminal where you started it. -Now that you have data in your index, you can chat with it using the CLI: +If you have started the server in the background, you can stop it by running: ```bash -canopy chat +canopy stop ``` -This will open a chat interface in your terminal. You can ask questions and the **Canopy** will try to answer them using the data you uploaded. +## Evaluation chat tool -To compare the chat response with and without RAG use the `--baseline` flag +Canopy's CLI comes with a built-in chat app that allows you to interactively chat with your text data and compare RAG vs. non-RAG workflows side-by-side to evaluate the results -```bash -canopy chat --baseline -``` +In a new terminal window, set the [required environment variables](#setup) then run: -This will open a similar chat interface window, but will send your question directly to the LLM without the RAG pipeline. +```bash -### 5. Stop the **Canopy** server +```bash +canopy chat +``` -To stop the server, simply press `CTRL+C` in the terminal where you started it. +This will open a chat interface in your terminal. You can ask questions and the RAG-infused chatbot will try to answer them using the data you uploaded. -If you have started the server in the background, you can stop it by running: +To compare the chat response with and without RAG use the `--no-rag` flag ```bash -canopy stop +canopy chat --no-rag ``` +This will open a similar chat interface window, but will show both the RAG and non-RAG responses side-by-side. + + ## Advanced usage ### Migrating existing OpenAI application to **Canopy** From 75c4a7a944a789c40342f7fbce36434062483cc3 Mon Sep 17 00:00:00 2001 From: ilai Date: Wed, 1 Nov 2023 19:48:53 +0200 Subject: [PATCH 10/20] minor typo --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index ce14c6fb..0ad3e87b 100644 --- a/README.md +++ b/README.md @@ -203,8 +203,6 @@ Canopy's CLI comes with a built-in chat app that allows you to interactively cha In a new terminal window, set the [required environment variables](#setup) then run: ```bash - -```bash canopy chat ``` From 6072fea527538674eab58b51a8cd9437856d2dfc Mon Sep 17 00:00:00 2001 From: Gibbs Cullen <109531614+gibbs-cullen@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:30:36 -0700 Subject: [PATCH 11/20] Update README.md Touched up some of the new wording --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 01e650e7..b96659fe 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Canopy -**Canopy** is an open-source Retrieval Augmented Generation (RAG) framework built on top of the Pinecone vector database. Canopy enables developers to quickly and easily experiment with and build applications using Retrieval Augmented Generation (RAG). -Canopy provides a configurable built-in server that allows users to effortlessly deploy a RAG-infused Chatbot web app using their own documents as a knowledge base. -For advanced use cases, the canopy core library enables building your own custom retrieval-powered AI applications. +**Canopy** is an open-source Retrieval Augmented Generation (RAG) framework and context engine built on top of the Pinecone vector database. Canopy enables you to quickly and easily experiment with and build applications using RAG. Start chatting with your documents or text data with a few simple commands. + +Canopy provides a configurable built-in server so you can effortlessly deploy a RAG-powered chat application to your existing chat UI or interface. Or you can build your own, custom RAG application using the Canopy lirbary. Canopy is desinged to be: * **Easy to implement:** Bring your text data in Parquet or JSONL format, and Canopy will handle the rest. Canopy makes it easy to incorporate RAG into your OpenAI chat applications. * **Reliable at scale:** Build fast, highly accurate GenAI applications that are production-ready and backed by Pinecone’s vector database. Seamlessly scale to billions of items with transarent, resource-based pricing. -* **Open and flexible:** Fully open-source, Canopy is both modular and extensible. You can configure to choose the components you need, or extend any component with your own custom implementation. Easily incorporate it into existing OpenAI applications and connect Canopy to your preferred UI. -* **Interactive and iterative:** Evaluate your RAG workflow with a CLI based chat tool. With a simple command in the Canopy CLI you can interactively chat with your text data and compare RAG vs. non-RAG workflows side-by-side to evaluate the augmented results before scaling to production. +* **Open and flexible:** Fully open-source, Canopy is both modular and extensible. You can configure to choose the components you need, or extend any component with your own custom implementation. +* **Interactive and iterative:** Evaluate your RAG workflow with a CLI based chat tool. With a simple command in the Canopy CLI you can interactively chat with your text data and compare RAG vs. non-RAG workflows side-by-side. ## RAG with Canopy @@ -48,15 +48,15 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and ## What's inside the box? -1. **Canopy Core Library** - Canopy has 3 API level components that are responsible for different parts of the RAG workflow: - * **ChatEngine** _`/chat/completions`_ - implements the full RAG workflow and exposes a chat interface to interact with your data. It acts as a wrapper around the Knowledge Base and Context Engine. - * **ContextEngine** - performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to be used as a prompt for the LLM. +1. **Canopy Core Library** - The library has 3 components or classes that are responsible for different parts of the RAG workflow: + * **ChatEngine** (_`/chat/completions`_) implements the full RAG workflow. It rewrites and transforms your queries into query embeddings before generating augmented search results (via the Context Engine) before returning them back to the end user. + * **ContextEngine** performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to augment the prompt for the LLM (via an OpenAI API endpoint). - * **KnowledgeBase** _`/context/{upsert, delete}` - prepares your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings before upserting them into the Pinecone vector database. It also handles Delete operations. + * **KnowledgeBase** (_`/context/{upsert, delete}`) prepares your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings before upserting them into the Pinecone vector database. It also handles Delete operations. > more information about the Core Library usage can be found in the [Library Documentation](docs/library.md) -2. **Canopy Service** - a webservice that wraps the **Canopy Core** and exposes it as a REST API. The service is built on top of FastAPI, Uvicorn and Gunicorn and can be easily deployed in production. The service also comes with a built in Swagger UI for easy testing and documentation. After you [start the server](#3-start-the-canopy-service), you can access the Swagger UI at `http://host:port/docs` (default: `http://localhost:8000/docs`) +2. **Canopy Service** - This is a webservice that wraps the **Canopy Core** library and exposes it as a REST API. The service is built on top of FastAPI, Uvicorn and Gunicorn and can be easily deployed in production. The service also comes with a built in Swagger UI for easy testing and documentation. After you [start the server](#3-start-the-canopy-service), you can access the Swagger UI at `http://host:port/docs` (default: `http://localhost:8000/docs`) 3. **Canopy CLI** - A built-in development tool that allows users to swiftly set up their own Canopy server and test its configuration. With just three CLI commands, you can create a new Canopy service, upload your documents to it, and then interact with the Chatbot using a built-in chat application directly from the terminal. The built-in chatbot also enables comparison of RAG-infused responses against a native LLM chatbot. @@ -65,7 +65,6 @@ With just three CLI commands, you can create a new Canopy service, upload your d * Canopy is currently only compatiable with OpenAI API endpoints for both the embedding model and the LLM. Rate limits and pricing set by OpenAI will apply. - ## Setup 0. set up a virtual environment (optional) From f7fce638ff350922dc364df4c9454d8a8bd1bfe5 Mon Sep 17 00:00:00 2001 From: ilai Date: Thu, 2 Nov 2023 10:16:37 +0200 Subject: [PATCH 12/20] [README] Restore the order of classes It probably makes more sense --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ad3e87b..080b6fff 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and ## What's inside the box? 1. **Canopy Core Library** - Canopy includes 3 main components that are responsible for different parts of the RAG workflow: + * **ChatEngine** - Exposes a chat interface to interact with your data. Given the history of chat messages, the `ChatEngine` formulates relevant queries to the `ContextEngine`, then uses the LLM to generate a knowledgeable response. + * **ContextEngine** - Performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant documents, then formulates a coherent textual context to be used as a prompt for the LLM. * **KnowledgeBase** - Manages your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings, storing them in a Pinecone vector database. Given a new textual query - the `KnowledgeBase` will retrieve the most relevant document chunks from the database. - * **ContextEngine** - Performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant document chunks, then formulates a coherent textual context to be used as a prompt for the LLM. - * **ChatEngine** - Exposes a chat interface to interact with your data. Given chat messages history, the `ChatEngine` uses the `ContextEngine` to generate a prompt and send it to an underlying LLM, returning a knowledge-augmented response. > more information about the Core Library usage can be found in the [Library Documentation](docs/library.md) From 49691447c267175892b1d57b08f5bd8dcfcf7e6a Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:22:50 +0200 Subject: [PATCH 13/20] ignore additioanl params in chat completion --- src/canopy_server/api_models.py | 3 +++ tests/e2e/test_app.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/canopy_server/api_models.py b/src/canopy_server/api_models.py index dae422f5..b0d61673 100644 --- a/src/canopy_server/api_models.py +++ b/src/canopy_server/api_models.py @@ -11,6 +11,9 @@ class ChatRequest(BaseModel): stream: bool = False user: Optional[str] = None + class Config: + extra = "ignore" + class ContextQueryRequest(BaseModel): queries: List[Query] diff --git a/tests/e2e/test_app.py b/tests/e2e/test_app.py index 6d067695..41357f4a 100644 --- a/tests/e2e/test_app.py +++ b/tests/e2e/test_app.py @@ -128,7 +128,7 @@ def test_query(client): upsert_payload.dict()["documents"][0]["source"]) -def test_chat(client): +def test_chat_required_params(client): # test response is as expected on /chat chat_payload = { "messages": [ @@ -149,6 +149,33 @@ def test_chat(client): assert all([kw in chat_response_content for kw in ["red", "bananas"]]) +def test_chat_openai_additional_params(client): + chat_payload = { + "messages": [ + { + "role": "user", + "content": "what is the topic of the test document? be concise", + } + ], + "user": "test-user", + "model": "gpt-4", + "temperature": 0.5, + "max_tokens": 10, + "logit_bias": {12: 10, 13: 11}, + "n": 2, + "stop": "stop string", + "top_p": 0.5, + } + chat_response = client.post("/context/chat/completions", json=chat_payload) + assert chat_response.is_success + chat_response_as_json = chat_response.json() + assert chat_response_as_json["choices"][0]["message"]["role"] == "assistant" + chat_response_content = chat_response_as_json["choices"][0]["message"][ + "content" + ] + assert all([kw in chat_response_content for kw in ["red", "bananas"]]) + + def test_delete(client, knowledge_base): doc_ids = ["api_tests-1"] vector_ids = [f"{d_id}_{0}" for d_id in doc_ids] From cfee71c84d7be294ca564d6f150975f7424e9654 Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:26:27 +0200 Subject: [PATCH 14/20] Update src/canopy/tokenizer/tokenizer.py Co-authored-by: igiloh-pinecone <118673156+igiloh-pinecone@users.noreply.github.com> --- src/canopy/tokenizer/tokenizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index 4a5d08f5..2724a124 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -17,7 +17,7 @@ class Tokenizer: >>> from canopy.tokenizer import Tokenizer >>> Tokenizer.initialize() - Then, you can init a tokenizer instance by calling Tokenizer() from anywhere in the code and use it: + Then, you can instantiate a tokenizer instance by calling Tokenizer() from anywhere in the code and use it: >>> tokenizer = Tokenizer() >>> tokenizer.tokenize("Hello world!") ['Hello', 'world', '!'] From 59eb77ef5971fb70a97387e4d18d34c689d36805 Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:26:33 +0200 Subject: [PATCH 15/20] Update src/canopy/tokenizer/tokenizer.py Co-authored-by: igiloh-pinecone <118673156+igiloh-pinecone@users.noreply.github.com> --- src/canopy/tokenizer/tokenizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index 2724a124..af4b414f 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -69,7 +69,7 @@ def initialize(cls, ... return sum([self.token_count(message) + 3 for message in messages]) >>> Tokenizer.initialize(MyTokenizer) - Then, you can init a tokenizer instance by calling Tokenizer() from anywhere in the code: + Then, you can instantiate a tokenizer instance by calling Tokenizer() from anywhere in the code: >>> from canopy.tokenizer import Tokenizer >>> tokenizer = Tokenizer() From 167569fcd397be8102c81312ac448eb9733f6013 Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:27:04 +0200 Subject: [PATCH 16/20] Update src/canopy/tokenizer/tokenizer.py Co-authored-by: igiloh-pinecone <118673156+igiloh-pinecone@users.noreply.github.com> --- src/canopy/tokenizer/tokenizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index af4b414f..8f4b6e0f 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -48,7 +48,7 @@ def initialize(cls, Args: tokenizer_class: The tokenizer class to use. Must be a subclass of BaseTokenizer. Defaults to OpenAITokenizer. - **kwargs: Keyword arguments to pass to the tokenizer class constructor. + **kwargs: Keyword arguments to pass to the underlying `Tokenizer` class constructor. Examples: Initialize the tokenizer with the default tokenizer class: From b6cccd876f1967c729748a29e2f1c2a4a0e12dfa Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:27:19 +0200 Subject: [PATCH 17/20] Update src/canopy/llm/openai.py Co-authored-by: igiloh-pinecone <118673156+igiloh-pinecone@users.noreply.github.com> --- src/canopy/llm/openai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canopy/llm/openai.py b/src/canopy/llm/openai.py index e5ffbfd6..208b0b5a 100644 --- a/src/canopy/llm/openai.py +++ b/src/canopy/llm/openai.py @@ -112,7 +112,7 @@ def enforced_function_call(self, max_tokens: Optional[int] = None, model_params: Optional[ModelParams] = None) -> dict: """ - This function enforces the model to response with a specific function call. + This function enforces the model to respond with a specific function call. To read more about this feature, see: https://platform.openai.com/docs/guides/gpt/function-calling From 5e1ad0b1764f33a80e4701a71e5e17e73c77fc27 Mon Sep 17 00:00:00 2001 From: Amnon Catav Date: Thu, 2 Nov 2023 12:31:18 +0200 Subject: [PATCH 18/20] add example --- src/canopy/tokenizer/tokenizer.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/canopy/tokenizer/tokenizer.py b/src/canopy/tokenizer/tokenizer.py index 4a5d08f5..61d1e90f 100644 --- a/src/canopy/tokenizer/tokenizer.py +++ b/src/canopy/tokenizer/tokenizer.py @@ -97,7 +97,15 @@ def initialize_from_config(cls, config: dict): Used by the config module to initialize the tokenizer from a config file. Args: - config: A dictionary containing the tokenizer configuration. Must contain a "type" key with the tokenizer class name. + config: A dictionary containing the tokenizer configuration. If not provided, the OpenAITokenizer will be used. + + Usage: + >>> from canopy.tokenizer import Tokenizer + >>> config = { + ... "type": "OpenAITokenizer", + ... "model_name": "gpt2" + ... } + >>> Tokenizer.initialize_from_config(config) """ # noqa: E501 if cls._initialized: raise ValueError("Tokenizer has already been initialized") From b7f1aea9cf44a516a7a88f8b493fc95fb9734f44 Mon Sep 17 00:00:00 2001 From: ilai Date: Thu, 2 Nov 2023 15:47:01 +0200 Subject: [PATCH 19/20] Changed library name to canopy-sdk Per new marketing decision --- README.md | 2 +- docs/library.md | 2 +- examples/canopy-lib-quickstart.ipynb | 2 +- pyproject.toml | 2 +- src/canopy/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 01e650e7..459f6c5f 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ more about virtual environments [here](https://docs.python.org/3/tutorial/venv.h 1. install the package ```bash -pip install pinecone-canopy +pip install canopy-sdk ``` 2. Set up the environment variables diff --git a/docs/library.md b/docs/library.md index d6d57b91..e832b195 100644 --- a/docs/library.md +++ b/docs/library.md @@ -30,7 +30,7 @@ more about virtual environments [here](https://docs.python.org/3/tutorial/venv.h 1. install the package ```bash -pip install pinecone-canopy +pip install canopy-sdk ``` 2. Set up the environment variables diff --git a/examples/canopy-lib-quickstart.ipynb b/examples/canopy-lib-quickstart.ipynb index 11764922..ac4981bf 100644 --- a/examples/canopy-lib-quickstart.ipynb +++ b/examples/canopy-lib-quickstart.ipynb @@ -38,7 +38,7 @@ } ], "source": [ - "!pip install -qU pinecone-canopy" + "!pip install -qU canopy-sdk" ] }, { diff --git a/pyproject.toml b/pyproject.toml index 0caf33e7..ed2d6ba0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "pinecone-canopy" +name = "canopy-sdk" version = "0.1.0" description = "Canopy is an orchestration engine for intergating LLMs with Pinecone." authors = ["Relevance Team "] diff --git a/src/canopy/__init__.py b/src/canopy/__init__.py index 95876e09..d4b5d255 100644 --- a/src/canopy/__init__.py +++ b/src/canopy/__init__.py @@ -1,4 +1,4 @@ import importlib.metadata # Taken from https://stackoverflow.com/a/67097076 -__version__ = importlib.metadata.version("pinecone-canopy") +__version__ = importlib.metadata.version("canopy-sdk") From 0fbe9798fb5cd11fd44a3e1eb884eece75a976c0 Mon Sep 17 00:00:00 2001 From: ilai Date: Thu, 2 Nov 2023 20:13:41 +0200 Subject: [PATCH 20/20] [README] Fix PR comments --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bdfd3c75..85093ade 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and
  • Canopy KnowledgeBase will encode each chunk using one or more embedding models
  • -
  • Canopy KnowledgeBase will upsert the encoded chunks into Pinecone Index
  • +
  • Canopy KnowledgeBase will upsert the encoded chunks into Pinecone index
  • @@ -51,7 +51,7 @@ Learn how Canopy implemenets the full RAG workflow to prevent hallucinations and 1. **Canopy Core Library** - The library has 3 main classes that are responsible for different parts of the RAG workflow: * **ChatEngine** - Exposes a chat interface to interact with your data. Given the history of chat messages, the `ChatEngine` formulates relevant queries to the `ContextEngine`, then uses the LLM to generate a knowledgeable response. * **ContextEngine** - Performs the “retrieval” part of RAG. The `ContextEngine` utilizes the underlying `KnowledgeBase` to retrieve the most relevant documents, then formulates a coherent textual context to be used as a prompt for the LLM. - * **KnowledgeBase** - Manages your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings, storing them in a Pinecone vector database. Given a new textual query - the `KnowledgeBase` will retrieve the most relevant document chunks from the database. + * **KnowledgeBase** - Manages your data for the RAG workflow. It automatically chunks and transforms your text data into text embeddings, storing them in a Pinecone vector database. Given a text query - the `KnowledgeBase` will retrieve the most relevant document chunks from the database. > more information about the Core Library usage can be found in the [Library Documentation](docs/library.md) @@ -125,7 +125,7 @@ In this quickstart, we will show you how to use the **Canopy** to build a simple ### 1. Create a new **Canopy** Index -As a one-time setup, Canopy needs to create and configure a new Pinecone index configured to work with Canopy. Just run: +As a one-time setup, Canopy needs to create a new Pinecone index that is configured to work with Canopy. Just run: ```bash canopy new @@ -134,11 +134,11 @@ canopy new And follow the CLI instructions. The index that will be created will have a prefix `canopy--`. You only have to do this process once for every Canopy index you want to create. -> To learn more about Pinecone Indexes and how to manage them, please refer to the following guide: [Understanding indexes](https://docs.pinecone.io/docs/indexes) +> To learn more about Pinecone indexes and how to manage them, please refer to the following guide: [Understanding indexes](https://docs.pinecone.io/docs/indexes) ### 2. Uploading data -You can load data into your Canopy Index using the command: +You can load data into your Canopy index using the command: ```bash canopy upsert /path/to/data_directory