Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
333eb8d60f | ||
|
|
414c69fd62 | ||
|
|
9951b58005 | ||
|
|
e73ed82ae9 | ||
|
|
8bde34a24f | ||
|
|
b58380e505 | ||
|
|
16f8de810c | ||
|
|
70f2de4fd3 | ||
|
|
a5d5e5825f | ||
|
|
0f16c72762 | ||
|
|
9ca7a0d6d1 | ||
|
|
7d5bfd8c9f | ||
|
|
cc9a06b116 | ||
|
|
b8fc7b0c9e | ||
|
|
3999f2a373 | ||
|
|
4eb2c0e123 | ||
|
|
b8a838aee1 | ||
|
|
84e5932ea5 | ||
|
|
e41573ca74 | ||
|
|
4c5c99e6ae | ||
|
|
dcb940ba95 | ||
|
|
8d3b66f7e3 |
@@ -7,8 +7,19 @@ PROJECT_NAME := embedchain
|
|||||||
.PHONY: install format lint clean test ci_lint ci_test
|
.PHONY: install format lint clean test ci_lint ci_test
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(PIP) install --upgrade pip
|
poetry install
|
||||||
$(PIP) install -e .[dev]
|
|
||||||
|
install_es:
|
||||||
|
poetry install --extras elasticsearch
|
||||||
|
|
||||||
|
install_opensearch:
|
||||||
|
poetry install --extras opensearch
|
||||||
|
|
||||||
|
shell:
|
||||||
|
poetry shell
|
||||||
|
|
||||||
|
py_shell:
|
||||||
|
poetry run python
|
||||||
|
|
||||||
format:
|
format:
|
||||||
$(PYTHON) -m black .
|
$(PYTHON) -m black .
|
||||||
|
|||||||
@@ -70,6 +70,6 @@ app.reset()
|
|||||||
Counts the number of embeddings (chunks) in the database.
|
Counts the number of embeddings (chunks) in the database.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
print(app.count())
|
print(app.db.count())
|
||||||
# returns: 481
|
# returns: 481
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: '💾 Vector Database'
|
title: '💾 Vector Database'
|
||||||
---
|
---
|
||||||
|
|
||||||
We support `Chroma` and `Elasticsearch` as two vector database.
|
We support `Chroma`, `Elasticsearch` and `OpenSearch` as vector databases.
|
||||||
`Chroma` is used as a default database.
|
`Chroma` is used as a default database.
|
||||||
|
|
||||||
## Elasticsearch
|
## Elasticsearch
|
||||||
@@ -22,13 +22,13 @@ Please note that the key needs certain privileges. For testing you can just togg
|
|||||||
2. Load the app
|
2. Load the app
|
||||||
```python
|
```python
|
||||||
from embedchain import CustomApp
|
from embedchain import CustomApp
|
||||||
from embedchain.embedder.openai import OpenAiEmbedder
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
from embedchain.llm.openai import OpenAILlm
|
from embedchain.llm.openai import OpenAILlm
|
||||||
from embedchain.vectordb.elasticsearch import ElasticsearchDB
|
from embedchain.vectordb.elasticsearch import ElasticsearchDB
|
||||||
|
|
||||||
es_app = CustomApp(
|
es_app = CustomApp(
|
||||||
llm=OpenAILlm(),
|
llm=OpenAILlm(),
|
||||||
embedder=OpenAiEmbedder(),
|
embedder=OpenAIEmbedder(),
|
||||||
db=ElasticsearchDB(),
|
db=ElasticsearchDB(),
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@@ -45,7 +45,7 @@ import os
|
|||||||
|
|
||||||
from embedchain import CustomApp
|
from embedchain import CustomApp
|
||||||
from embedchain.config import CustomAppConfig, ElasticsearchDBConfig
|
from embedchain.config import CustomAppConfig, ElasticsearchDBConfig
|
||||||
from embedchain.embedder.openai import OpenAiEmbedder
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
from embedchain.llm.openai import OpenAILlm
|
from embedchain.llm.openai import OpenAILlm
|
||||||
from embedchain.vectordb.elasticsearch import ElasticsearchDB
|
from embedchain.vectordb.elasticsearch import ElasticsearchDB
|
||||||
|
|
||||||
@@ -61,10 +61,58 @@ es_config = ElasticsearchDBConfig(
|
|||||||
es_app = CustomApp(
|
es_app = CustomApp(
|
||||||
config=CustomAppConfig(log_level="INFO"),
|
config=CustomAppConfig(log_level="INFO"),
|
||||||
llm=OpenAILlm(),
|
llm=OpenAILlm(),
|
||||||
embedder=OpenAiEmbedder(),
|
embedder=OpenAIEmbedder(),
|
||||||
db=ElasticsearchDB(config=es_config),
|
db=ElasticsearchDB(config=es_config),
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
3. This should log your connection details to the console.
|
3. This should log your connection details to the console.
|
||||||
4. Alternatively to a URL, you `ElasticsearchDBConfig` accepts `es_url` as a list of nodes url with different hosts and ports.
|
4. Alternatively to a URL, you `ElasticsearchDBConfig` accepts `es_url` as a list of nodes url with different hosts and ports.
|
||||||
5. Additionally we can pass named parameters supported by Python Elasticsearch client.
|
5. Additionally we can pass named parameters supported by Python Elasticsearch client.
|
||||||
|
|
||||||
|
|
||||||
|
## OpenSearch 🔍
|
||||||
|
|
||||||
|
To use OpenSearch as a vector database with a CustomApp, follow these simple steps:
|
||||||
|
|
||||||
|
1. Set the `OPENAI_API_KEY` environment variable:
|
||||||
|
|
||||||
|
```
|
||||||
|
OPENAI_API_KEY=sk-xxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Define the OpenSearch configuration in your Python code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from embedchain import CustomApp
|
||||||
|
from embedchain.config import OpenSearchDBConfig
|
||||||
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
|
from embedchain.llm.openai import OpenAILlm
|
||||||
|
from embedchain.vectordb.opensearch import OpenSearchDB
|
||||||
|
|
||||||
|
opensearch_url = "https://localhost:9200"
|
||||||
|
http_auth = ("username", "password")
|
||||||
|
|
||||||
|
db_config = OpenSearchDBConfig(
|
||||||
|
opensearch_url=opensearch_url,
|
||||||
|
http_auth=http_auth,
|
||||||
|
collection_name="embedchain-app",
|
||||||
|
use_ssl=True,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
db = OpenSearchDB(config=db_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Instantiate the app and add data:
|
||||||
|
|
||||||
|
```python
|
||||||
|
app = CustomApp(llm=OpenAILlm(), embedder=OpenAIEmbedder(), db=db)
|
||||||
|
app.add("https://en.wikipedia.org/wiki/Elon_Musk")
|
||||||
|
app.add("https://www.forbes.com/profile/elon-musk")
|
||||||
|
app.add("https://www.britannica.com/biography/Elon-Musk")
|
||||||
|
```
|
||||||
|
|
||||||
|
3. You're all set! Start querying using the following command:
|
||||||
|
|
||||||
|
```python
|
||||||
|
app.query("What is the net worth of Elon Musk?")
|
||||||
|
```
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 421 KiB |
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
title: '🛠️ LangSmith'
|
||||||
|
description: 'Integrate with Langsmith to debug and monitor your LLM app'
|
||||||
|
---
|
||||||
|
|
||||||
|
Embedchain now supports integration with [LangSmith](https://www.langchain.com/langsmith).
|
||||||
|
|
||||||
|
To use langsmith, you need to do the following steps
|
||||||
|
|
||||||
|
1. Have an account on langsmith and keep the environment variables in handy
|
||||||
|
2. Set the environments variables in your app so that embedchain has context about it.
|
||||||
|
3. Just use embedchain and everything will be logged to LangSmith, so that you can better test and monitor your application.
|
||||||
|
|
||||||
|
Lets cover each step in detail.
|
||||||
|
|
||||||
|
* First make sure that you a LangSmith account created and have all the necessary variables handy. LangSmith has a [good documentation](https://docs.smith.langchain.com/) on how to get started with their service.
|
||||||
|
|
||||||
|
* Once you have the account setup, we will need the following environment variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LANGCHAIN_TRACING_V2=true
|
||||||
|
export LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
|
||||||
|
export LANGCHAIN_API_KEY=<your-api-key>
|
||||||
|
export LANGCHAIN_PROJECT=<your-project> # if not specified, defaults to "default"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using Python, you can use the following code to set environment variables
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ['LANGCHAIN_TRACING_V2'] = 'true'
|
||||||
|
os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com'
|
||||||
|
os.environ['LANGCHAIN_API_KEY'] = <your-api-key>
|
||||||
|
os.environ['LANGCHAIN_PROJECT] = <your-project>
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now create an app using embedchain and everything will be automatically visible in the LangSmith
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
from embedchain import App
|
||||||
|
|
||||||
|
app = App()
|
||||||
|
app.add("https://en.wikipedia.org/wiki/Elon_Musk")
|
||||||
|
app.query("How many companies did Elon found?")
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now the entire log for this will be visible in langsmith.
|
||||||
|
|
||||||
|
<img src="/images/langsmith.png"/>
|
||||||
+10
-1
@@ -19,6 +19,10 @@
|
|||||||
{
|
{
|
||||||
"name": "Discord",
|
"name": "Discord",
|
||||||
"url": "https://discord.gg/6PzXDgEjG5"
|
"url": "https://discord.gg/6PzXDgEjG5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Slack",
|
||||||
|
"url":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"topbarCtaButton": {
|
"topbarCtaButton": {
|
||||||
@@ -38,6 +42,10 @@
|
|||||||
"group": "Examples",
|
"group": "Examples",
|
||||||
"pages": ["examples/full_stack", "examples/api_server", "examples/discord_bot", "examples/slack_bot", "examples/telegram_bot", "examples/whatsapp_bot", "examples/poe_bot"]
|
"pages": ["examples/full_stack", "examples/api_server", "examples/discord_bot", "examples/slack_bot", "examples/telegram_bot", "examples/whatsapp_bot", "examples/poe_bot"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "Integration",
|
||||||
|
"pages": ["integration/langsmith"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Contribution Guidelines",
|
"group": "Contribution Guidelines",
|
||||||
"pages": ["contribution/dev", "contribution/docs"]
|
"pages": ["contribution/dev", "contribution/docs"]
|
||||||
@@ -48,7 +56,8 @@
|
|||||||
"twitter": "https://twitter.com/embedchain",
|
"twitter": "https://twitter.com/embedchain",
|
||||||
"github": "https://github.com/embedchain/embedchain",
|
"github": "https://github.com/embedchain/embedchain",
|
||||||
"linkedin": "https://www.linkedin.com/company/embedchain",
|
"linkedin": "https://www.linkedin.com/company/embedchain",
|
||||||
"website": "https://embedchain.ai"
|
"website": "https://embedchain.ai",
|
||||||
|
"slack":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw"
|
||||||
},
|
},
|
||||||
"backgroundImage": "/background.png",
|
"backgroundImage": "/background.png",
|
||||||
"isWhiteLabeled": true
|
"isWhiteLabeled": true
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import importlib.metadata
|
|||||||
|
|
||||||
__version__ = importlib.metadata.version(__package__ or __name__)
|
__version__ = importlib.metadata.version(__package__ or __name__)
|
||||||
|
|
||||||
from embedchain.apps.App import App # noqa: F401
|
from embedchain.apps.app import App # noqa: F401
|
||||||
from embedchain.apps.CustomApp import CustomApp # noqa: F401
|
from embedchain.apps.custom_app import CustomApp # noqa: F401
|
||||||
from embedchain.apps.Llama2App import Llama2App # noqa: F401
|
from embedchain.apps.Llama2App import Llama2App # noqa: F401
|
||||||
from embedchain.apps.OpenSourceApp import OpenSourceApp # noqa: F401
|
from embedchain.apps.open_source_app import OpenSourceApp # noqa: F401
|
||||||
from embedchain.apps.PersonApp import (PersonApp, # noqa: F401
|
from embedchain.apps.PersonApp import (PersonApp, # noqa: F401
|
||||||
PersonOpenSourceApp)
|
PersonOpenSourceApp)
|
||||||
from embedchain.vectordb.chroma import ChromaDB # noqa: F401
|
from embedchain.vectordb.chroma import ChromaDB # noqa: F401
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from embedchain.apps.CustomApp import CustomApp
|
from embedchain.apps.custom_app import CustomApp
|
||||||
from embedchain.config import CustomAppConfig
|
from embedchain.config import CustomAppConfig
|
||||||
from embedchain.embedder.openai import OpenAiEmbedder
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
from embedchain.llm.llama2 import Llama2Llm
|
from embedchain.llm.llama2 import Llama2Llm
|
||||||
from embedchain.vectordb.chroma import ChromaDB
|
from embedchain.vectordb.chroma import ChromaDB
|
||||||
@@ -29,5 +29,5 @@ class Llama2App(CustomApp):
|
|||||||
config = CustomAppConfig()
|
config = CustomAppConfig()
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
config=config, llm=Llama2Llm(), db=ChromaDB(), embedder=OpenAiEmbedder(), system_prompt=system_prompt
|
config=config, llm=Llama2Llm(), db=ChromaDB(), embedder=OpenAIEmbedder(), system_prompt=system_prompt
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from string import Template
|
from string import Template
|
||||||
|
|
||||||
from embedchain.apps.App import App
|
from embedchain.apps.app import App
|
||||||
from embedchain.apps.OpenSourceApp import OpenSourceApp
|
from embedchain.apps.open_source_app import OpenSourceApp
|
||||||
from embedchain.config import BaseLlmConfig
|
from embedchain.config import BaseLlmConfig
|
||||||
from embedchain.config.apps.BaseAppConfig import BaseAppConfig
|
from embedchain.config.apps.base_app_config import BaseAppConfig
|
||||||
from embedchain.config.llm.base_llm_config import (DEFAULT_PROMPT,
|
from embedchain.config.llm.base_llm_config import (DEFAULT_PROMPT,
|
||||||
DEFAULT_PROMPT_WITH_HISTORY)
|
DEFAULT_PROMPT_WITH_HISTORY)
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from embedchain.config import (AppConfig, BaseEmbedderConfig, BaseLlmConfig,
|
from embedchain.config import (AppConfig, BaseEmbedderConfig, BaseLlmConfig,
|
||||||
ChromaDbConfig)
|
ChromaDbConfig)
|
||||||
from embedchain.embedchain import EmbedChain
|
from embedchain.embedchain import EmbedChain
|
||||||
from embedchain.embedder.openai import OpenAiEmbedder
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
from embedchain.llm.openai import OpenAILlm
|
from embedchain.llm.openai import OpenAILlm
|
||||||
from embedchain.vectordb.chroma import ChromaDB
|
from embedchain.vectordb.chroma import ChromaDB
|
||||||
@@ -48,7 +48,7 @@ class App(EmbedChain):
|
|||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
|
|
||||||
llm = OpenAILlm(config=llm_config)
|
llm = OpenAILlm(config=llm_config)
|
||||||
embedder = OpenAiEmbedder(config=BaseEmbedderConfig(model="text-embedding-ada-002"))
|
embedder = OpenAIEmbedder(config=BaseEmbedderConfig(model="text-embedding-ada-002"))
|
||||||
database = ChromaDB(config=chromadb_config)
|
database = ChromaDB(config=chromadb_config)
|
||||||
|
|
||||||
super().__init__(config, llm, db=database, embedder=embedder, system_prompt=system_prompt)
|
super().__init__(config, llm, db=database, embedder=embedder, system_prompt=system_prompt)
|
||||||
@@ -2,7 +2,7 @@ from typing import Any
|
|||||||
|
|
||||||
from embedchain import CustomApp
|
from embedchain import CustomApp
|
||||||
from embedchain.config import AddConfig, CustomAppConfig, LlmConfig
|
from embedchain.config import AddConfig, CustomAppConfig, LlmConfig
|
||||||
from embedchain.embedder.openai import OpenAiEmbedder
|
from embedchain.embedder.openai import OpenAIEmbedder
|
||||||
from embedchain.helper.json_serializable import (JSONSerializable,
|
from embedchain.helper.json_serializable import (JSONSerializable,
|
||||||
register_deserializable)
|
register_deserializable)
|
||||||
from embedchain.llm.openai import OpenAILlm
|
from embedchain.llm.openai import OpenAILlm
|
||||||
@@ -12,7 +12,7 @@ from embedchain.vectordb.chroma import ChromaDB
|
|||||||
@register_deserializable
|
@register_deserializable
|
||||||
class BaseBot(JSONSerializable):
|
class BaseBot(JSONSerializable):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = CustomApp(config=CustomAppConfig(), llm=OpenAILlm(), db=ChromaDB(), embedder=OpenAiEmbedder())
|
self.app = CustomApp(config=CustomAppConfig(), llm=OpenAILlm(), db=ChromaDB(), embedder=OpenAIEmbedder())
|
||||||
|
|
||||||
def add(self, data: Any, config: AddConfig = None):
|
def add(self, data: Any, config: AddConfig = None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
|
|
||||||
|
|
||||||
class TableChunker(BaseChunker):
|
class TableChunker(BaseChunker):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config.AddConfig import ChunkerConfig
|
from embedchain.config.add_config import ChunkerConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
|
|
||||||
from .AddConfig import AddConfig, ChunkerConfig
|
from .add_config import AddConfig, ChunkerConfig
|
||||||
from .apps.AppConfig import AppConfig
|
from .apps.app_config import AppConfig
|
||||||
from .apps.CustomAppConfig import CustomAppConfig
|
from .apps.custom_app_config import CustomAppConfig
|
||||||
from .apps.OpenSourceAppConfig import OpenSourceAppConfig
|
from .apps.open_source_app_config import OpenSourceAppConfig
|
||||||
from .BaseConfig import BaseConfig
|
from .base_config import BaseConfig
|
||||||
from .embedder.BaseEmbedderConfig import BaseEmbedderConfig
|
from .embedder.base import BaseEmbedderConfig
|
||||||
from .embedder.BaseEmbedderConfig import BaseEmbedderConfig as EmbedderConfig
|
from .embedder.base import BaseEmbedderConfig as EmbedderConfig
|
||||||
from .llm.base_llm_config import BaseLlmConfig
|
from .llm.base_llm_config import BaseLlmConfig
|
||||||
from .llm.base_llm_config import BaseLlmConfig as LlmConfig
|
from .llm.base_llm_config import BaseLlmConfig as LlmConfig
|
||||||
from .vectordbs.ChromaDbConfig import ChromaDbConfig
|
from .vectordb.chroma import ChromaDbConfig
|
||||||
from .vectordbs.ElasticsearchDBConfig import ElasticsearchDBConfig
|
from .vectordb.elasticsearch import ElasticsearchDBConfig
|
||||||
|
from .vectordb.opensearch import OpenSearchDBConfig
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|
||||||
from embedchain.config.BaseConfig import BaseConfig
|
from embedchain.config.base_config import BaseConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
from .BaseAppConfig import BaseAppConfig
|
from .base_app_config import BaseAppConfig
|
||||||
|
|
||||||
|
|
||||||
@register_deserializable
|
@register_deserializable
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from embedchain.config.BaseConfig import BaseConfig
|
from embedchain.config.base_config import BaseConfig
|
||||||
from embedchain.helper.json_serializable import JSONSerializable
|
from embedchain.helper.json_serializable import JSONSerializable
|
||||||
from embedchain.vectordb.base import BaseVectorDB
|
from embedchain.vectordb.base import BaseVectorDB
|
||||||
|
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from dotenv import load_dotenv
|
|||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
from embedchain.vectordb.base import BaseVectorDB
|
from embedchain.vectordb.base import BaseVectorDB
|
||||||
|
|
||||||
from .BaseAppConfig import BaseAppConfig
|
from .base_app_config import BaseAppConfig
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
from .BaseAppConfig import BaseAppConfig
|
from .base_app_config import BaseAppConfig
|
||||||
|
|
||||||
|
|
||||||
@register_deserializable
|
@register_deserializable
|
||||||
@@ -2,7 +2,7 @@ import re
|
|||||||
from string import Template
|
from string import Template
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from embedchain.config.BaseConfig import BaseConfig
|
from embedchain.config.base_config import BaseConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
DEFAULT_PROMPT = """
|
DEFAULT_PROMPT = """
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from embedchain.config.BaseConfig import BaseConfig
|
from embedchain.config.base_config import BaseConfig
|
||||||
|
|
||||||
|
|
||||||
class BaseVectorDbConfig(BaseConfig):
|
class BaseVectorDbConfig(BaseConfig):
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from embedchain.config.vectordbs.BaseVectorDbConfig import BaseVectorDbConfig
|
from embedchain.config.vectordb.base import BaseVectorDbConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Dict, List, Optional, Union
|
from typing import Dict, List, Optional, Union
|
||||||
|
|
||||||
from embedchain.config.vectordbs.BaseVectorDbConfig import BaseVectorDbConfig
|
from embedchain.config.vectordb.base import BaseVectorDbConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from typing import Dict, Optional, Tuple
|
||||||
|
|
||||||
|
from embedchain.config.vectordb.base import BaseVectorDbConfig
|
||||||
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
|
||||||
|
|
||||||
|
@register_deserializable
|
||||||
|
class OpenSearchDBConfig(BaseVectorDbConfig):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
opensearch_url: str,
|
||||||
|
http_auth: Tuple[str, str],
|
||||||
|
vector_dimension: int = 1536,
|
||||||
|
collection_name: Optional[str] = None,
|
||||||
|
dir: Optional[str] = None,
|
||||||
|
**extra_params: Dict[str, any],
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initializes a configuration class instance for an OpenSearch client.
|
||||||
|
|
||||||
|
:param collection_name: Default name for the collection, defaults to None
|
||||||
|
:type collection_name: Optional[str], optional
|
||||||
|
:param opensearch_url: URL of the OpenSearch domain
|
||||||
|
:type opensearch_url: str, Eg, "http://localhost:9200"
|
||||||
|
:param http_auth: Tuple of username and password
|
||||||
|
:type http_auth: Tuple[str, str], Eg, ("username", "password")
|
||||||
|
:param vector_dimension: Dimension of the vector, defaults to 1536 (openai embedding model)
|
||||||
|
:type vector_dimension: int, optional
|
||||||
|
:param dir: Path to the database directory, where the database is stored, defaults to None
|
||||||
|
:type dir: Optional[str], optional
|
||||||
|
"""
|
||||||
|
self.opensearch_url = opensearch_url
|
||||||
|
self.http_auth = http_auth
|
||||||
|
self.vector_dimension = vector_dimension
|
||||||
|
self.extra_params = extra_params
|
||||||
|
|
||||||
|
super().__init__(collection_name=collection_name, dir=dir)
|
||||||
@@ -10,7 +10,7 @@ from embedchain.chunkers.text import TextChunker
|
|||||||
from embedchain.chunkers.web_page import WebPageChunker
|
from embedchain.chunkers.web_page import WebPageChunker
|
||||||
from embedchain.chunkers.youtube_video import YoutubeVideoChunker
|
from embedchain.chunkers.youtube_video import YoutubeVideoChunker
|
||||||
from embedchain.config import AddConfig
|
from embedchain.config import AddConfig
|
||||||
from embedchain.config.AddConfig import ChunkerConfig, LoaderConfig
|
from embedchain.config.add_config import ChunkerConfig, LoaderConfig
|
||||||
from embedchain.helper.json_serializable import JSONSerializable
|
from embedchain.helper.json_serializable import JSONSerializable
|
||||||
from embedchain.loaders.base_loader import BaseLoader
|
from embedchain.loaders.base_loader import BaseLoader
|
||||||
from embedchain.loaders.csv import CsvLoader
|
from embedchain.loaders.csv import CsvLoader
|
||||||
|
|||||||
+10
-99
@@ -6,7 +6,7 @@ import os
|
|||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
@@ -15,13 +15,14 @@ from tenacity import retry, stop_after_attempt, wait_fixed
|
|||||||
|
|
||||||
from embedchain.chunkers.base_chunker import BaseChunker
|
from embedchain.chunkers.base_chunker import BaseChunker
|
||||||
from embedchain.config import AddConfig, BaseLlmConfig
|
from embedchain.config import AddConfig, BaseLlmConfig
|
||||||
from embedchain.config.apps.BaseAppConfig import BaseAppConfig
|
from embedchain.config.apps.base_app_config import BaseAppConfig
|
||||||
from embedchain.data_formatter import DataFormatter
|
from embedchain.data_formatter import DataFormatter
|
||||||
from embedchain.embedder.base import BaseEmbedder
|
from embedchain.embedder.base import BaseEmbedder
|
||||||
from embedchain.helper.json_serializable import JSONSerializable
|
from embedchain.helper.json_serializable import JSONSerializable
|
||||||
from embedchain.llm.base import BaseLlm
|
from embedchain.llm.base import BaseLlm
|
||||||
from embedchain.loaders.base_loader import BaseLoader
|
from embedchain.loaders.base_loader import BaseLoader
|
||||||
from embedchain.models.data_type import DataType, DirectDataType, IndirectDataType, SpecialDataType
|
from embedchain.models.data_type import (DataType, DirectDataType,
|
||||||
|
IndirectDataType, SpecialDataType)
|
||||||
from embedchain.utils import detect_datatype
|
from embedchain.utils import detect_datatype
|
||||||
from embedchain.vectordb.base import BaseVectorDB
|
from embedchain.vectordb.base import BaseVectorDB
|
||||||
|
|
||||||
@@ -60,16 +61,13 @@ class EmbedChain(JSONSerializable):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
# Llm
|
||||||
# Add subclasses
|
|
||||||
## Llm
|
|
||||||
self.llm = llm
|
self.llm = llm
|
||||||
## Database
|
|
||||||
# Database has support for config assignment for backwards compatibility
|
# Database has support for config assignment for backwards compatibility
|
||||||
if db is None and (not hasattr(self.config, "db") or self.config.db is None):
|
if db is None and (not hasattr(self.config, "db") or self.config.db is None):
|
||||||
raise ValueError("App requires Database.")
|
raise ValueError("App requires Database.")
|
||||||
self.db = db or self.config.db
|
self.db = db or self.config.db
|
||||||
## Embedder
|
# Embedder
|
||||||
if embedder is None:
|
if embedder is None:
|
||||||
raise ValueError("App requires Embedder.")
|
raise ValueError("App requires Embedder.")
|
||||||
self.embedder = embedder
|
self.embedder = embedder
|
||||||
@@ -200,7 +198,7 @@ class EmbedChain(JSONSerializable):
|
|||||||
|
|
||||||
data_formatter = DataFormatter(data_type, config)
|
data_formatter = DataFormatter(data_type, config)
|
||||||
self.user_asks.append([source, data_type.value, metadata])
|
self.user_asks.append([source, data_type.value, metadata])
|
||||||
documents, metadatas, _ids, new_chunks = self.load_and_embed_v2(
|
documents, metadatas, _ids, new_chunks = self.load_and_embed(
|
||||||
data_formatter.loader, data_formatter.chunker, source, metadata, source_id, dry_run
|
data_formatter.loader, data_formatter.chunker, source, metadata, source_id, dry_run
|
||||||
)
|
)
|
||||||
if data_type in {DataType.DOCS_SITE}:
|
if data_type in {DataType.DOCS_SITE}:
|
||||||
@@ -255,93 +253,6 @@ class EmbedChain(JSONSerializable):
|
|||||||
)
|
)
|
||||||
return self.add(source=source, data_type=data_type, metadata=metadata, config=config)
|
return self.add(source=source, data_type=data_type, metadata=metadata, config=config)
|
||||||
|
|
||||||
def load_and_embed(
|
|
||||||
self,
|
|
||||||
loader: BaseLoader,
|
|
||||||
chunker: BaseChunker,
|
|
||||||
src: Any,
|
|
||||||
metadata: Optional[Dict[str, Any]] = None,
|
|
||||||
source_id: Optional[str] = None,
|
|
||||||
dry_run=False,
|
|
||||||
) -> Tuple[List[str], Dict[str, Any], List[str], int]:
|
|
||||||
"""The loader to use to load the data.
|
|
||||||
|
|
||||||
:param loader: The loader to use to load the data.
|
|
||||||
:type loader: BaseLoader
|
|
||||||
:param chunker: The chunker to use to chunk the data.
|
|
||||||
:type chunker: BaseChunker
|
|
||||||
:param src: The data to be handled by the loader.
|
|
||||||
Can be a URL for remote sources or local content for local loaders.
|
|
||||||
:type src: Any
|
|
||||||
:param metadata: Metadata associated with the data source., defaults to None
|
|
||||||
:type metadata: Dict[str, Any], optional
|
|
||||||
:param source_id: Hexadecimal hash of the source., defaults to None
|
|
||||||
:type source_id: str, optional
|
|
||||||
:param dry_run: Optional. A dry run returns chunks and doesn't update DB.
|
|
||||||
:type dry_run: bool, defaults to False
|
|
||||||
:return: (List) documents (embedded text), (List) metadata, (list) ids, (int) number of chunks
|
|
||||||
:rtype: Tuple[List[str], Dict[str, Any], List[str], int]
|
|
||||||
"""
|
|
||||||
embeddings_data = chunker.create_chunks(loader, src)
|
|
||||||
|
|
||||||
# spread chunking results
|
|
||||||
documents = embeddings_data["documents"]
|
|
||||||
metadatas = embeddings_data["metadatas"]
|
|
||||||
ids = embeddings_data["ids"]
|
|
||||||
|
|
||||||
# get existing ids, and discard doc if any common id exist.
|
|
||||||
where = {"app_id": self.config.id} if self.config.id is not None else {}
|
|
||||||
# where={"url": src}
|
|
||||||
db_result = self.db.get(
|
|
||||||
ids=ids,
|
|
||||||
where=where, # optional filter
|
|
||||||
)
|
|
||||||
existing_ids = set(db_result["ids"])
|
|
||||||
|
|
||||||
if len(existing_ids):
|
|
||||||
data_dict = {id: (doc, meta) for id, doc, meta in zip(ids, documents, metadatas)}
|
|
||||||
data_dict = {id: value for id, value in data_dict.items() if id not in existing_ids}
|
|
||||||
|
|
||||||
if not data_dict:
|
|
||||||
src_copy = src
|
|
||||||
if len(src_copy) > 50:
|
|
||||||
src_copy = src[:50] + "..."
|
|
||||||
print(f"All data from {src_copy} already exists in the database.")
|
|
||||||
# Make sure to return a matching return type
|
|
||||||
return [], [], [], 0
|
|
||||||
|
|
||||||
ids = list(data_dict.keys())
|
|
||||||
documents, metadatas = zip(*data_dict.values())
|
|
||||||
|
|
||||||
# Loop though all metadatas and add extras.
|
|
||||||
new_metadatas = []
|
|
||||||
for m in metadatas:
|
|
||||||
# Add app id in metadatas so that they can be queried on later
|
|
||||||
if self.config.id:
|
|
||||||
m["app_id"] = self.config.id
|
|
||||||
|
|
||||||
# Add hashed source
|
|
||||||
m["hash"] = source_id
|
|
||||||
|
|
||||||
# Note: Metadata is the function argument
|
|
||||||
if metadata:
|
|
||||||
# Spread whatever is in metadata into the new object.
|
|
||||||
m.update(metadata)
|
|
||||||
|
|
||||||
new_metadatas.append(m)
|
|
||||||
metadatas = new_metadatas
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
return list(documents), metadatas, ids, 0
|
|
||||||
|
|
||||||
# Count before, to calculate a delta in the end.
|
|
||||||
chunks_before_addition = self.db.count()
|
|
||||||
|
|
||||||
self.db.add(documents=documents, metadatas=metadatas, ids=ids)
|
|
||||||
count_new_chunks = self.db.count() - chunks_before_addition
|
|
||||||
print((f"Successfully saved {src} ({chunker.data_type}). New chunks count: {count_new_chunks}"))
|
|
||||||
return list(documents), metadatas, ids, count_new_chunks
|
|
||||||
|
|
||||||
def _get_existing_doc_id(self, chunker: BaseChunker, src: Any):
|
def _get_existing_doc_id(self, chunker: BaseChunker, src: Any):
|
||||||
"""
|
"""
|
||||||
Get id of existing document for a given source, based on the data type
|
Get id of existing document for a given source, based on the data type
|
||||||
@@ -392,7 +303,7 @@ class EmbedChain(JSONSerializable):
|
|||||||
"When it should be DirectDataType, IndirectDataType or SpecialDataType."
|
"When it should be DirectDataType, IndirectDataType or SpecialDataType."
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_and_embed_v2(
|
def load_and_embed(
|
||||||
self,
|
self,
|
||||||
loader: BaseLoader,
|
loader: BaseLoader,
|
||||||
chunker: BaseChunker,
|
chunker: BaseChunker,
|
||||||
@@ -480,10 +391,10 @@ class EmbedChain(JSONSerializable):
|
|||||||
return list(documents), metadatas, ids, 0
|
return list(documents), metadatas, ids, 0
|
||||||
|
|
||||||
# Count before, to calculate a delta in the end.
|
# Count before, to calculate a delta in the end.
|
||||||
chunks_before_addition = self.count()
|
chunks_before_addition = self.db.count()
|
||||||
|
|
||||||
self.db.add(documents=documents, metadatas=metadatas, ids=ids)
|
self.db.add(documents=documents, metadatas=metadatas, ids=ids)
|
||||||
count_new_chunks = self.count() - chunks_before_addition
|
count_new_chunks = self.db.count() - chunks_before_addition
|
||||||
print((f"Successfully saved {src} ({chunker.data_type}). New chunks count: {count_new_chunks}"))
|
print((f"Successfully saved {src} ({chunker.data_type}). New chunks count: {count_new_chunks}"))
|
||||||
return list(documents), metadatas, ids, count_new_chunks
|
return list(documents), metadatas, ids, count_new_chunks
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
from embedchain.config.embedder.BaseEmbedderConfig import BaseEmbedderConfig
|
from embedchain.config.embedder.base import BaseEmbedderConfig
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from chromadb.api.types import Documents, Embeddings
|
from chromadb.api.types import Documents, Embeddings
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ except RuntimeError:
|
|||||||
from chromadb.utils import embedding_functions
|
from chromadb.utils import embedding_functions
|
||||||
|
|
||||||
|
|
||||||
class OpenAiEmbedder(BaseEmbedder):
|
class OpenAIEmbedder(BaseEmbedder):
|
||||||
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
|
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
|
||||||
super().__init__(config=config)
|
super().__init__(config=config)
|
||||||
if self.config.model is None:
|
if self.config.model is None:
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ class AzureOpenAILlm(BaseLlm):
|
|||||||
super().__init__(config=config)
|
super().__init__(config=config)
|
||||||
|
|
||||||
def get_llm_model_answer(self, prompt):
|
def get_llm_model_answer(self, prompt):
|
||||||
return AzureOpenAILlm._get_azure_openai_answer(prompt=prompt, config=self.config)
|
return AzureOpenAILlm._get_answer(prompt=prompt, config=self.config)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_azure_openai_answer(prompt: str, config: BaseLlmConfig) -> str:
|
def _get_answer(prompt: str, config: BaseLlmConfig) -> str:
|
||||||
from langchain.chat_models import AzureChatOpenAI
|
from langchain.chat_models import AzureChatOpenAI
|
||||||
|
|
||||||
if not config.deployment_name:
|
if not config.deployment_name:
|
||||||
|
|||||||
+26
-24
@@ -1,6 +1,7 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import openai
|
from langchain.chat_models import ChatOpenAI
|
||||||
|
from langchain.schema import HumanMessage, SystemMessage
|
||||||
|
|
||||||
from embedchain.config import BaseLlmConfig
|
from embedchain.config import BaseLlmConfig
|
||||||
from embedchain.helper.json_serializable import register_deserializable
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
@@ -12,31 +13,32 @@ class OpenAILlm(BaseLlm):
|
|||||||
def __init__(self, config: Optional[BaseLlmConfig] = None):
|
def __init__(self, config: Optional[BaseLlmConfig] = None):
|
||||||
super().__init__(config=config)
|
super().__init__(config=config)
|
||||||
|
|
||||||
# NOTE: This class does not use langchain. One reason is that `top_p` is not supported.
|
|
||||||
|
|
||||||
def get_llm_model_answer(self, prompt):
|
def get_llm_model_answer(self, prompt):
|
||||||
messages = []
|
response = OpenAILlm._get_answer(prompt, self.config)
|
||||||
if self.config.system_prompt:
|
|
||||||
messages.append({"role": "system", "content": self.config.system_prompt})
|
|
||||||
messages.append({"role": "user", "content": prompt})
|
|
||||||
response = openai.ChatCompletion.create(
|
|
||||||
model=self.config.model or "gpt-3.5-turbo-0613",
|
|
||||||
messages=messages,
|
|
||||||
temperature=self.config.temperature,
|
|
||||||
max_tokens=self.config.max_tokens,
|
|
||||||
top_p=self.config.top_p,
|
|
||||||
stream=self.config.stream,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.config.stream:
|
if self.config.stream:
|
||||||
return self._stream_llm_model_response(response)
|
return response
|
||||||
else:
|
else:
|
||||||
return response["choices"][0]["message"]["content"]
|
return response.content
|
||||||
|
|
||||||
def _stream_llm_model_response(self, response):
|
def _get_answer(prompt: str, config: BaseLlmConfig) -> str:
|
||||||
"""
|
messages = []
|
||||||
This is a generator for streaming response from the OpenAI completions API
|
if config.system_prompt:
|
||||||
"""
|
messages.append(SystemMessage(content=config.system_prompt))
|
||||||
for line in response:
|
messages.append(HumanMessage(content=prompt))
|
||||||
chunk = line["choices"][0].get("delta", {}).get("content", "")
|
kwargs = {
|
||||||
yield chunk
|
"model": config.model or "gpt-3.5-turbo-0613",
|
||||||
|
"temperature": config.temperature,
|
||||||
|
"max_tokens": config.max_tokens,
|
||||||
|
"model_kwargs": {},
|
||||||
|
}
|
||||||
|
if config.top_p:
|
||||||
|
kwargs["model_kwargs"]["top_p"] = config.top_p
|
||||||
|
if config.stream:
|
||||||
|
from langchain.callbacks.streaming_stdout import \
|
||||||
|
StreamingStdOutCallbackHandler
|
||||||
|
|
||||||
|
chat = ChatOpenAI(**kwargs, streaming=config.stream, callbacks=[StreamingStdOutCallbackHandler()])
|
||||||
|
else:
|
||||||
|
chat = ChatOpenAI(**kwargs)
|
||||||
|
return chat(messages)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from .EmbeddingFunctions import EmbeddingFunctions # noqa: F401
|
from .embedding_functions import EmbeddingFunctions # noqa: F401
|
||||||
from .Providers import Providers # noqa: F401
|
from .providers import Providers # noqa: F401
|
||||||
from .VectorDatabases import VectorDatabases # noqa: F401
|
from .vector_databases import VectorDatabases # noqa: F401
|
||||||
from .VectorDimensions import VectorDimensions # noqa: F401
|
from .vector_dimensions import VectorDimensions # noqa: F401
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ from enum import Enum
|
|||||||
class VectorDatabases(Enum):
|
class VectorDatabases(Enum):
|
||||||
CHROMADB = "CHROMADB"
|
CHROMADB = "CHROMADB"
|
||||||
ELASTICSEARCH = "ELASTICSEARCH"
|
ELASTICSEARCH = "ELASTICSEARCH"
|
||||||
|
OPENSEARCH = "OPENSEARCH"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from embedchain.config.vectordbs.BaseVectorDbConfig import BaseVectorDbConfig
|
from embedchain.config.vectordb.base import BaseVectorDbConfig
|
||||||
from embedchain.embedder.base import BaseEmbedder
|
from embedchain.embedder.base import BaseEmbedder
|
||||||
from embedchain.helper.json_serializable import JSONSerializable
|
from embedchain.helper.json_serializable import JSONSerializable
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
from embedchain.config.vectordbs.BaseVectorDbConfig import BaseVectorDbConfig
|
|
||||||
from embedchain.embedder.base_embedder import BaseEmbedder
|
|
||||||
from embedchain.helper_classes.json_serializable import JSONSerializable
|
|
||||||
|
|
||||||
|
|
||||||
class BaseVectorDB(JSONSerializable):
|
|
||||||
"""Base class for vector database."""
|
|
||||||
|
|
||||||
def __init__(self, config: BaseVectorDbConfig):
|
|
||||||
self.client = self._get_or_create_db()
|
|
||||||
self.config: BaseVectorDbConfig = config
|
|
||||||
|
|
||||||
def _initialize(self):
|
|
||||||
"""
|
|
||||||
This method is needed because `embedder` attribute needs to be set externally before it can be initialized.
|
|
||||||
|
|
||||||
So it's can't be done in __init__ in one step.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _get_or_create_db(self):
|
|
||||||
"""Get or create the database."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _get_or_create_collection(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _set_embedder(self, embedder: BaseEmbedder):
|
|
||||||
self.embedder = embedder
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def add(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def query(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def count(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def set_collection_name(self, name: str):
|
|
||||||
raise NotImplementedError
|
|
||||||
@@ -63,7 +63,9 @@ class ChromaDB(BaseVectorDB):
|
|||||||
This method is needed because `embedder` attribute needs to be set externally before it can be initialized.
|
This method is needed because `embedder` attribute needs to be set externally before it can be initialized.
|
||||||
"""
|
"""
|
||||||
if not self.embedder:
|
if not self.embedder:
|
||||||
raise ValueError("Embedder not set. Please set an embedder with `set_embedder` before initialization.")
|
raise ValueError(
|
||||||
|
"Embedder not set. Please set an embedder with `_set_embedder()` function before initialization."
|
||||||
|
)
|
||||||
self._get_or_create_collection(self.config.collection_name)
|
self._get_or_create_collection(self.config.collection_name)
|
||||||
|
|
||||||
def _get_or_create_db(self):
|
def _get_or_create_db(self):
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Dict, List, Optional, Set
|
||||||
|
|
||||||
|
try:
|
||||||
|
from opensearchpy import OpenSearch
|
||||||
|
from opensearchpy.helpers import bulk
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError(
|
||||||
|
"OpenSearch requires extra dependencies. Install with `pip install --upgrade embedchain[opensearch]`"
|
||||||
|
) from None
|
||||||
|
|
||||||
|
from langchain.embeddings.openai import OpenAIEmbeddings
|
||||||
|
from langchain.vectorstores import OpenSearchVectorSearch
|
||||||
|
|
||||||
|
from embedchain.config import OpenSearchDBConfig
|
||||||
|
from embedchain.helper.json_serializable import register_deserializable
|
||||||
|
from embedchain.vectordb.base import BaseVectorDB
|
||||||
|
|
||||||
|
|
||||||
|
@register_deserializable
|
||||||
|
class OpenSearchDB(BaseVectorDB):
|
||||||
|
"""
|
||||||
|
OpenSearch as vector database
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config: OpenSearchDBConfig):
|
||||||
|
"""OpenSearch as vector database.
|
||||||
|
|
||||||
|
:param config: OpenSearch domain config
|
||||||
|
:type config: OpenSearchDBConfig
|
||||||
|
"""
|
||||||
|
if config is None:
|
||||||
|
raise ValueError("OpenSearchDBConfig is required")
|
||||||
|
self.config = config
|
||||||
|
self.client = OpenSearch(
|
||||||
|
hosts=[self.config.opensearch_url],
|
||||||
|
http_auth=self.config.http_auth,
|
||||||
|
**self.config.extra_params,
|
||||||
|
)
|
||||||
|
info = self.client.info()
|
||||||
|
logging.info(f"Connected to {info['version']['distribution']}. Version: {info['version']['number']}")
|
||||||
|
# Remove auth credentials from config after successful connection
|
||||||
|
super().__init__(config=self.config)
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
logging.info(self.client.info())
|
||||||
|
index_name = self._get_index()
|
||||||
|
if self.client.indices.exists(index=index_name):
|
||||||
|
print(f"Index '{index_name}' already exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
index_body = {
|
||||||
|
"settings": {"knn": True},
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"text": {"type": "text"},
|
||||||
|
"embeddings": {
|
||||||
|
"type": "knn_vector",
|
||||||
|
"index": False,
|
||||||
|
"dimension": self.config.vector_dimension,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.client.indices.create(index_name, body=index_body)
|
||||||
|
print(self.client.indices.get(index_name))
|
||||||
|
|
||||||
|
def _get_or_create_db(self):
|
||||||
|
"""Called during initialization"""
|
||||||
|
return self.client
|
||||||
|
|
||||||
|
def _get_or_create_collection(self, name):
|
||||||
|
"""Note: nothing to return here. Discuss later"""
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self, ids: Optional[List[str]] = None, where: Optional[Dict[str, any]] = None, limit: Optional[int] = None
|
||||||
|
) -> Set[str]:
|
||||||
|
"""
|
||||||
|
Get existing doc ids present in vector database
|
||||||
|
|
||||||
|
:param ids: _list of doc ids to check for existence
|
||||||
|
:type ids: List[str]
|
||||||
|
:param where: to filter data
|
||||||
|
:type where: Dict[str, any]
|
||||||
|
:return: ids
|
||||||
|
:type: Set[str]
|
||||||
|
"""
|
||||||
|
if ids:
|
||||||
|
query = {"query": {"bool": {"must": [{"ids": {"values": ids}}]}}}
|
||||||
|
else:
|
||||||
|
query = {"query": {"bool": {"must": []}}}
|
||||||
|
if "app_id" in where:
|
||||||
|
app_id = where["app_id"]
|
||||||
|
query["query"]["bool"]["must"].append({"term": {"metadata.app_id": app_id}})
|
||||||
|
|
||||||
|
# OpenSearch syntax is different from Elasticsearch
|
||||||
|
response = self.client.search(index=self._get_index(), body=query, _source=False, size=limit)
|
||||||
|
docs = response["hits"]["hits"]
|
||||||
|
ids = [doc["_id"] for doc in docs]
|
||||||
|
return {"ids": set(ids)}
|
||||||
|
|
||||||
|
def add(self, documents: List[str], metadatas: List[object], ids: List[str]):
|
||||||
|
"""add data in vector database
|
||||||
|
|
||||||
|
:param documents: list of texts to add
|
||||||
|
:type documents: List[str]
|
||||||
|
:param metadatas: list of metadata associated with docs
|
||||||
|
:type metadatas: List[object]
|
||||||
|
:param ids: ids of docs
|
||||||
|
:type ids: List[str]
|
||||||
|
"""
|
||||||
|
|
||||||
|
docs = []
|
||||||
|
embeddings = self.embedder.embedding_fn(documents)
|
||||||
|
for id, text, metadata, embeddings in zip(ids, documents, metadatas, embeddings):
|
||||||
|
docs.append(
|
||||||
|
{
|
||||||
|
"_index": self._get_index(),
|
||||||
|
"_id": id,
|
||||||
|
"_source": {"text": text, "metadata": metadata, "embeddings": embeddings},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
bulk(self.client, docs)
|
||||||
|
self.client.indices.refresh(index=self._get_index())
|
||||||
|
|
||||||
|
def query(self, input_query: List[str], n_results: int, where: Dict[str, any]) -> List[str]:
|
||||||
|
"""
|
||||||
|
query contents from vector data base based on vector similarity
|
||||||
|
|
||||||
|
:param input_query: list of query string
|
||||||
|
:type input_query: List[str]
|
||||||
|
:param n_results: no of similar documents to fetch from database
|
||||||
|
:type n_results: int
|
||||||
|
:param where: Optional. to filter data
|
||||||
|
:type where: Dict[str, any]
|
||||||
|
:return: Database contents that are the result of the query
|
||||||
|
:rtype: List[str]
|
||||||
|
"""
|
||||||
|
embeddings = OpenAIEmbeddings()
|
||||||
|
docsearch = OpenSearchVectorSearch(
|
||||||
|
index_name=self._get_index(),
|
||||||
|
embedding_function=embeddings,
|
||||||
|
opensearch_url=f"{self.config.opensearch_url}",
|
||||||
|
http_auth=self.config.http_auth,
|
||||||
|
use_ssl=True,
|
||||||
|
)
|
||||||
|
docs = docsearch.similarity_search(
|
||||||
|
input_query,
|
||||||
|
search_type="script_scoring",
|
||||||
|
space_type="cosinesimil",
|
||||||
|
vector_field="embeddings",
|
||||||
|
text_field="text",
|
||||||
|
metadata_field="metadata",
|
||||||
|
)
|
||||||
|
contents = [doc.page_content for doc in docs]
|
||||||
|
return contents
|
||||||
|
|
||||||
|
def set_collection_name(self, name: str):
|
||||||
|
"""
|
||||||
|
Set the name of the collection. A collection is an isolated space for vectors.
|
||||||
|
|
||||||
|
:param name: Name of the collection.
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
|
if not isinstance(name, str):
|
||||||
|
raise TypeError("Collection name must be a string")
|
||||||
|
self.config.collection_name = name
|
||||||
|
|
||||||
|
def count(self) -> int:
|
||||||
|
"""
|
||||||
|
Count number of documents/chunks embedded in the database.
|
||||||
|
|
||||||
|
:return: number of documents
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
query = {"query": {"match_all": {}}}
|
||||||
|
response = self.client.count(index=self._get_index(), body=query)
|
||||||
|
doc_count = response["count"]
|
||||||
|
return doc_count
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Resets the database. Deletes all embeddings irreversibly.
|
||||||
|
"""
|
||||||
|
# Delete all data from the database
|
||||||
|
if self.client.indices.exists(index=self._get_index()):
|
||||||
|
# delete index in Es
|
||||||
|
self.client.indices.delete(index=self._get_index())
|
||||||
|
|
||||||
|
def _get_index(self) -> str:
|
||||||
|
"""Get the OpenSearch index for a collection
|
||||||
|
|
||||||
|
:return: OpenSearch index
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self.config.collection_name
|
||||||
+4
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "embedchain"
|
name = "embedchain"
|
||||||
version = "0.0.59"
|
version = "0.0.62"
|
||||||
description = "embedchain is a framework to easily create LLM powered bots over any dataset"
|
description = "embedchain is a framework to easily create LLM powered bots over any dataset"
|
||||||
authors = ["Taranjeet Singh"]
|
authors = ["Taranjeet Singh"]
|
||||||
license = "Apache License"
|
license = "Apache License"
|
||||||
@@ -98,12 +98,14 @@ torch = { version = ">=2.0.0, !=2.0.1", optional = true }
|
|||||||
# Torch 2.0.1 is not compatible with poetry (https://github.com/pytorch/pytorch/issues/100974)
|
# Torch 2.0.1 is not compatible with poetry (https://github.com/pytorch/pytorch/issues/100974)
|
||||||
gpt4all = { version = "1.0.8", optional = true }
|
gpt4all = { version = "1.0.8", optional = true }
|
||||||
# 1.0.9 is not working for some users (https://github.com/nomic-ai/gpt4all/issues/1394)
|
# 1.0.9 is not working for some users (https://github.com/nomic-ai/gpt4all/issues/1394)
|
||||||
|
opensearch-py = { version = "2.3.1", optional = true }
|
||||||
elasticsearch = { version = "^8.9.0", optional = true }
|
elasticsearch = { version = "^8.9.0", optional = true }
|
||||||
flask = { version = "^2.3.3", optional = true }
|
flask = { version = "^2.3.3", optional = true }
|
||||||
twilio = { version = "^8.5.0", optional = true }
|
twilio = { version = "^8.5.0", optional = true }
|
||||||
fastapi-poe = { version = "0.0.16", optional = true }
|
fastapi-poe = { version = "0.0.16", optional = true }
|
||||||
discord = { version = "^2.3.2", optional = true }
|
discord = { version = "^2.3.2", optional = true }
|
||||||
slack-sdk = { version = "3.21.3", optional = true }
|
slack-sdk = { version = "3.21.3", optional = true }
|
||||||
|
docx2txt = "^0.8"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -122,6 +124,7 @@ streamlit = ["streamlit"]
|
|||||||
community = ["llama-hub"]
|
community = ["llama-hub"]
|
||||||
opensource = ["sentence-transformers", "torch", "gpt4all"]
|
opensource = ["sentence-transformers", "torch", "gpt4all"]
|
||||||
elasticsearch = ["elasticsearch"]
|
elasticsearch = ["elasticsearch"]
|
||||||
|
opensearch = ["opensearch-py"]
|
||||||
poe = ["fastapi-poe"]
|
poe = ["fastapi-poe"]
|
||||||
discord = ["discord"]
|
discord = ["discord"]
|
||||||
slack = ["slack-sdk", "flask"]
|
slack = ["slack-sdk", "flask"]
|
||||||
|
|||||||
+13
-25
@@ -46,41 +46,29 @@ class TestApp(unittest.TestCase):
|
|||||||
self.assertEqual(input_query_arg, "Test query")
|
self.assertEqual(input_query_arg, "Test query")
|
||||||
mock_answer.assert_called_once()
|
mock_answer.assert_called_once()
|
||||||
|
|
||||||
@patch("openai.ChatCompletion.create")
|
@patch("embedchain.llm.openai.OpenAILlm._get_answer")
|
||||||
def test_query_config_app_passing(self, mock_create):
|
def test_query_config_app_passing(self, mock_get_answer):
|
||||||
mock_create.return_value = {"choices": [{"message": {"content": "response"}}]} # Mock response
|
mock_get_answer.return_value = MagicMock()
|
||||||
|
mock_get_answer.return_value.content = "Test answer"
|
||||||
|
|
||||||
config = AppConfig(collect_metrics=False)
|
config = AppConfig(collect_metrics=False)
|
||||||
chat_config = BaseLlmConfig(system_prompt="Test system prompt")
|
chat_config = BaseLlmConfig(system_prompt="Test system prompt")
|
||||||
app = App(config=config, llm_config=chat_config)
|
app = App(config=config, llm_config=chat_config)
|
||||||
|
answer = app.llm.get_llm_model_answer("Test query")
|
||||||
|
|
||||||
app.llm.get_llm_model_answer("Test query")
|
self.assertEqual(app.llm.config.system_prompt, "Test system prompt")
|
||||||
|
self.assertEqual(answer, "Test answer")
|
||||||
# Test system_prompt: Check that the 'create' method was called with the correct 'messages' argument
|
|
||||||
messages_arg = mock_create.call_args.kwargs["messages"]
|
|
||||||
self.assertTrue(messages_arg[0].get("role"), "system")
|
|
||||||
self.assertEqual(messages_arg[0].get("content"), "Test system prompt")
|
|
||||||
self.assertTrue(messages_arg[1].get("role"), "user")
|
|
||||||
self.assertEqual(messages_arg[1].get("content"), "Test query")
|
|
||||||
|
|
||||||
# TODO: Add tests for other config variables
|
|
||||||
|
|
||||||
@patch("openai.ChatCompletion.create")
|
|
||||||
def test_app_passing(self, mock_create):
|
|
||||||
mock_create.return_value = {"choices": [{"message": {"content": "response"}}]} # Mock response
|
|
||||||
|
|
||||||
|
@patch("embedchain.llm.openai.OpenAILlm._get_answer")
|
||||||
|
def test_app_passing(self, mock_get_answer):
|
||||||
|
mock_get_answer.return_value = MagicMock()
|
||||||
|
mock_get_answer.return_value.content = "Test answer"
|
||||||
config = AppConfig(collect_metrics=False)
|
config = AppConfig(collect_metrics=False)
|
||||||
chat_config = BaseLlmConfig()
|
chat_config = BaseLlmConfig()
|
||||||
app = App(config=config, llm_config=chat_config, system_prompt="Test system prompt")
|
app = App(config=config, llm_config=chat_config, system_prompt="Test system prompt")
|
||||||
|
answer = app.llm.get_llm_model_answer("Test query")
|
||||||
self.assertEqual(app.llm.config.system_prompt, "Test system prompt")
|
self.assertEqual(app.llm.config.system_prompt, "Test system prompt")
|
||||||
|
self.assertEqual(answer, "Test answer")
|
||||||
app.llm.get_llm_model_answer("Test query")
|
|
||||||
|
|
||||||
# Test system_prompt: Check that the 'create' method was called with the correct 'messages' argument
|
|
||||||
messages_arg = mock_create.call_args.kwargs["messages"]
|
|
||||||
self.assertTrue(messages_arg[0].get("role"), "system")
|
|
||||||
self.assertEqual(messages_arg[0].get("content"), "Test system prompt")
|
|
||||||
|
|
||||||
@patch("chromadb.api.models.Collection.Collection.add", MagicMock)
|
@patch("chromadb.api.models.Collection.Collection.add", MagicMock)
|
||||||
def test_query_with_where_in_params(self):
|
def test_query_with_where_in_params(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user