mirror of
https://github.com/Cinnamon/kotaemon.git
synced 2025-06-26 23:19:56 +00:00

* move flowsettings.py and launch.py to root * update docs * sync sub package versions * rename launch.py to app.py and make run scripts work with installation package * add update scripts * auto version for root package * rename authors and update doc dir * Update auto-bump-and-release.yaml to trigger on push to main branch * latest as branch instead of tag * pin deps versions * cache the changelogs
233 lines
6.7 KiB
Markdown
233 lines
6.7 KiB
Markdown
# 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
|
|
```
|