Spaces:
Paused
Paused
| # Add new indexing and reasoning pipeline to the application | |
| @trducng | |
| At high level, to add new indexing and reasoning pipeline: | |
| 1. You define your indexing or reasoning pipeline as a class from | |
| `BaseComponent`. | |
| 2. You declare that class in the setting files `flowsettings.py`. | |
| Then when `python app.py`, the application will dynamically load those | |
| pipelines. | |
| The below sections talk in more detail about how the pipelines should be | |
| constructed. | |
| ## Define a pipeline as a class | |
| In essence, a pipeline will subclass from `kotaemon.base.BaseComponent`. | |
| Each pipeline has 2 main parts: | |
| - All declared arguments and sub-pipelines. | |
| - The logic inside the pipeline. | |
| An example pipeline: | |
| ```python | |
| from kotaemon.base import BaseComponent | |
| class SoSimple(BaseComponent): | |
| arg1: int | |
| arg2: str | |
| def run(self, arg3: str): | |
| return self.arg1 * self.arg2 + arg3 | |
| ``` | |
| This pipeline is simple for demonstration purpose, but we can imagine pipelines | |
| with much more arguments, that can take other pipelines as arguments, and have | |
| more complicated logic in the `run` method. | |
| **_An indexing or reasoning pipeline is just a class subclass from | |
| `BaseComponent` like above._** | |
| For more detail on this topic, please refer to [Creating a | |
| Component](/create-a-component/) | |
| ## Run signatures | |
| **Note**: this section is tentative at the moment. We will finalize `def run` | |
| function signature by latest early April. | |
| The indexing pipeline: | |
| ```python | |
| def run( | |
| self, | |
| file_paths: str | Path | list[str | Path], | |
| reindex: bool = False, | |
| **kwargs, | |
| ): | |
| """Index files to intermediate representation (e.g. vector, database...) | |
| Args: | |
| file_paths: the list of paths to files | |
| reindex: if True, files in `file_paths` that already exists in database | |
| should be reindex. | |
| """ | |
| ``` | |
| The reasoning pipeline: | |
| ```python | |
| def run(self, question: str, history: list, **kwargs) -> Document: | |
| """Answer the question | |
| Args: | |
| question: the user input | |
| history: the chat history [(user_msg1, bot_msg1), (user_msg2, bot_msg2)...] | |
| Returns: | |
| kotaemon.base.Document: the final answer | |
| """ | |
| ``` | |
| ## Register your pipeline to ktem | |
| To register your pipelines to ktem, you declare it in the `flowsettings.py` | |
| file. This file locates at the current working directory where you start the | |
| ktem. In most use cases, it is this | |
| [one](https://github.com/Cinnamon/kotaemon/blob/main/libs/ktem/flowsettings.py). | |
| ```python | |
| KH_REASONING = ["<python.module.path.to.the.reasoning.class>"] | |
| KH_INDEX = "<python.module.path.to.the.indexing.class>" | |
| ``` | |
| You can register multiple reasoning pipelines to ktem by populating the | |
| `KH_REASONING` list. The user can select which reasoning pipeline to use | |
| in their Settings page. | |
| For now, there's only one supported index option for `KH_INDEX`. | |
| Make sure that your class is discoverable by Python. | |
| ## Allow users to customize your pipeline in the app settings | |
| To allow the users to configure your pipeline, you need to declare what you | |
| allow the users to configure as a dictionary. `ktem` will include them into the | |
| application settings. | |
| In your pipeline class, add a classmethod `get_user_settings` that returns a | |
| setting dictionary, add a classmethod `get_info` that returns an info | |
| dictionary. Example: | |
| ```python | |
| class SoSimple(BaseComponent): | |
| ... # as above | |
| @classmethod | |
| def get_user_settings(cls) -> dict: | |
| """The settings to the user""" | |
| return { | |
| "setting_1": { | |
| "name": "Human-friendly name", | |
| "value": "Default value", | |
| "choices": [("Human-friendly Choice 1", "choice1-id"), ("HFC 2", "choice2-id")], # optional | |
| "component": "Which Gradio UI component to render, can be: text, number, checkbox, dropdown, radio, checkboxgroup" | |
| }, | |
| "setting_2": { | |
| # follow the same rule as above | |
| } | |
| } | |
| @classmethod | |
| def get_info(cls) -> dict: | |
| """Pipeline information for bookkeeping purpose""" | |
| return { | |
| "id": "a unique id to differentiate this pipeline from other pipeline", | |
| "name": "Human-friendly name of the pipeline", | |
| "description": "Can be a short description of this pipeline" | |
| } | |
| ``` | |
| Once adding these methods to your pipeline class, `ktem` will automatically | |
| extract and add them to the settings. | |
| ## Construct to pipeline object | |
| Once `ktem` runs your pipeline, it will call your classmethod `get_pipeline` | |
| with the full user settings and expect to obtain the pipeline object. Within | |
| this `get_pipeline` method, you implement all the necessary logics to initiate | |
| the pipeline object. Example: | |
| ```python | |
| class SoSimple(BaseComponent): | |
| ... # as above | |
| @classmethod | |
| def get_pipeline(self, setting): | |
| obj = cls(arg1=setting["reasoning.id.setting1"]) | |
| return obj | |
| ``` | |
| ## Reasoning: Stream output to UI | |
| For fast user experience, you can stream the output directly to UI. This way, | |
| user can start observing the output as soon as the LLM model generates the 1st | |
| token, rather than having to wait the pipeline finishes to read the whole message. | |
| To stream the output, you need to; | |
| 1. Turn the `run` function to async. | |
| 2. Pass in the output to a special queue with `self.report_output`. | |
| ```python | |
| async def run(self, question: str, history: list, **kwargs) -> Document: | |
| for char in "This is a long messages": | |
| self.report_output({"output": text.text}) | |
| ``` | |
| The argument to `self.report_output` is a dictionary, that contains either or | |
| all of these 2 keys: "output", "evidence". The "output" string will be streamed | |
| to the chat message, and the "evidence" string will be streamed to the | |
| information panel. | |
| ## Access application LLMs, Embeddings | |
| You can access users' collections of LLMs and embedding models with: | |
| ```python | |
| from ktem.embeddings.manager import embeddings | |
| from ktem.llms.manager import llms | |
| llm = llms.get_default() | |
| embedding_model = embeddings.get_default() | |
| ``` | |
| You can also allow the users to specifically select which llms or embedding | |
| models they want to use through the settings. | |
| ```python | |
| @classmethod | |
| def get_user_settings(cls) -> dict: | |
| from ktem.llms.manager import llms | |
| return { | |
| "citation_llm": { | |
| "name": "LLM for citation", | |
| "value": llms.get_default(), | |
| "component: "dropdown", | |
| "choices": list(llms.options().keys()), | |
| }, | |
| ... | |
| } | |
| ``` | |
| ## Optional: Access application data | |
| You can access the user's application database, vector store as follow: | |
| ```python | |
| # get the database that contains the source files | |
| from ktem.db.models import Source, Index, Conversation, User | |
| # get the vector store | |
| ``` | |