Compare commits

..
22 Commits
Author SHA1 Message Date
e84b5034ea [Bugfix] fix return type of ec chat (#995)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-06 10:00:19 -08:00
a4831d6ed9 Bump package version to 0.1.27 (#994)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-06 00:03:13 -08:00
51b4966801 [Improvements] Package improvements (#993)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 23:42:45 -08:00
atkinshandGitHub 1d4e00ccef local file path support for sitemap loader (#992) 2023-12-05 19:04:20 -08:00
c9fbc2e7d6 fix sitemap loader (#986)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 17:56:24 -08:00
Deshraj YadavandGitHub fa34788df6 Bump package version to 0.1.26 (#991) 2023-12-05 16:52:48 -08:00
0f4f220119 Package improvements (#989)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 16:48:00 -08:00
Deven PatelandGitHub 512cfc9466 [Improvement] customize add method (#988) 2023-12-05 00:55:33 -08:00
Deshraj YadavandGitHub 541b1cb7c7 Update version to 0.1.25 (#985) 2023-12-03 15:15:52 -08:00
Deven PatelandGitHub 36af1a7615 [Improvement] improve github loader (#984) 2023-12-01 11:24:13 -08:00
Deshraj YadavandGitHub b02e8feeda [BugFix] Fix issue of chunks not getting embedded in opensearch index (#983) 2023-11-29 21:56:05 -08:00
Deven PatelandGitHub 406c46e7f4 [Improvements] Add support for creating app from YAML string config (#980) 2023-11-29 12:25:30 -08:00
Sidharth MohantyandGitHub e35eaf1bfc Improve deps installation by converting them to one liner (#967) 2023-11-29 10:08:34 -08:00
Sidharth MohantyandGitHub 38426a7af1 Discord loader (#976) 2023-11-29 10:07:05 -08:00
Deshraj YadavandGitHub 141a23fb1e [BugFix] Skip checking thread when making sqlite connection (#978) 2023-11-26 15:44:06 -08:00
Sidharth MohantyandGitHub bb28569abf Update workflow to run when required (#941) 2023-11-24 09:29:31 -08:00
Deshraj YadavandGitHub 1df46b2bb3 [Bug fix] Fix issue of missing user directory (#975) 2023-11-24 09:26:59 -08:00
Deshraj YadavandGitHub 58f72e1ffe Update Azure OpenAI embedding model docs (#974) 2023-11-23 01:45:46 -08:00
Deshraj YadavandGitHub 33409140b4 [Bug fix] Fix Azure OpenAI related issue (#973) 2023-11-23 01:40:54 -08:00
Deshraj YadavandGitHub f6b80e01a1 [Feature] Add support for custom streaming callback (#971) 2023-11-22 01:06:33 -08:00
Sidharth MohantyandGitHub 798d3fcc5a Update version to 0.1.18 (#970) 2023-11-21 10:01:09 -08:00
Sidharth MohantyandGitHub 85f3ac428b Update embedding_fn signature to newest chroma db's (#969) 2023-11-21 09:42:11 -08:00
131 changed files with 1440 additions and 776 deletions
+8
View File
@@ -3,7 +3,15 @@ name: ci
on: on:
push: push:
branches: [main] branches: [main]
paths:
- 'embedchain/**'
- 'tests/**'
- 'examples/**'
pull_request: pull_request:
paths:
- 'embedchain/**'
- 'tests/**'
- 'examples/**'
jobs: jobs:
build: build:
-1
View File
@@ -23,4 +23,3 @@ embedder:
provider: openai provider: openai
config: config:
model: 'text-embedding-ada-002' model: 'text-embedding-ada-002'
deployment_name: 'test-deployment'
+121 -20
View File
@@ -6,15 +6,16 @@ Embedchain is made to work out of the box. However, for advanced users we're als
You can configure different components of your app (`llm`, `embedding model`, or `vector database`) through a simple yaml configuration that Embedchain offers. Here is a generic full-stack example of the yaml config: You can configure different components of your app (`llm`, `embedding model`, or `vector database`) through a simple yaml configuration that Embedchain offers. Here is a generic full-stack example of the yaml config:
```yaml
<Tip>
Embedchain applications are configurable using YAML file, JSON file or by directly passing the config dictionary.
</Tip>
<CodeGroup>
```yaml config.yaml
app: app:
config: config:
id: 'full-stack-app' name: 'full-stack-app'
chunker:
chunk_size: 100
chunk_overlap: 20
length_function: 'len'
llm: llm:
provider: openai provider: openai
@@ -47,38 +48,138 @@ embedder:
provider: openai provider: openai
config: config:
model: 'text-embedding-ada-002' model: 'text-embedding-ada-002'
chunker:
chunk_size: 2000
chunk_overlap: 100
length_function: 'len'
``` ```
```json config.json
{
"app": {
"config": {
"name": "full-stack-app"
}
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-3.5-turbo",
"temperature": 0.5,
"max_tokens": 1000,
"top_p": 1,
"stream": false,
"template": "Use the following pieces of context to answer the query at the end.\nIf you don't know the answer, just say that you don't know, don't try to make up an answer.\n$context\n\nQuery: $query\n\nHelpful Answer:",
"system_prompt": "Act as William Shakespeare. Answer the following questions in the style of William Shakespeare."
}
},
"vectordb": {
"provider": "chroma",
"config": {
"collection_name": "full-stack-app",
"dir": "db",
"allow_reset": true
}
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-ada-002"
}
},
"chunker": {
"chunk_size": 2000,
"chunk_overlap": 100,
"length_function": "len"
}
}
```
```python config.py
config = {
'app': {
'config': {
'name': 'full-stack-app'
}
},
'llm': {
'provider': 'openai',
'config': {
'model': 'gpt-3.5-turbo',
'temperature': 0.5,
'max_tokens': 1000,
'top_p': 1,
'stream': False,
'template': (
"Use the following pieces of context to answer the query at the end.\n"
"If you don't know the answer, just say that you don't know, don't try to make up an answer.\n"
"$context\n\nQuery: $query\n\nHelpful Answer:"
),
'system_prompt': (
"Act as William Shakespeare. Answer the following questions in the style of William Shakespeare."
)
}
},
'vectordb': {
'provider': 'chroma',
'config': {
'collection_name': 'full-stack-app',
'dir': 'db',
'allow_reset': True
}
},
'embedder': {
'provider': 'openai',
'config': {
'model': 'text-embedding-ada-002'
}
},
'chunker': {
'chunk_size': 2000,
'chunk_overlap': 100,
'length_function': 'len'
}
}
```
</CodeGroup>
Alright, let's dive into what each key means in the yaml config above: Alright, let's dive into what each key means in the yaml config above:
1. `app` Section: 1. `app` Section:
- `config`: - `config`:
- `id` (String): The ID or name of your full-stack application. - `name` (String): The name of your full-stack application.
2. `chunker` Section: - `id` (String): The id of your full-stack application.
- `chunk_size` (Integer): The size of each chunk of text that is sent to the language model. <Note>Only use this to reload already created apps. We recommend users to not create their own ids.</Note>
- `chunk_overlap` (Integer): The amount of overlap between each chunk of text. - `collect_metrics` (Boolean): Indicates whether metrics should be collected for the app, defaults to `True`
- `length_function` (String): The function used to calculate the length of each chunk of text. In this case, it's set to 'len'. You can also use any function import directly as a string here. - `log_level` (String): The log level for the app, defaults to `WARNING`
3. `llm` Section: 2. `llm` Section:
- `provider` (String): The provider for the language model, which is set to 'openai'. You can find the full list of llm providers in [our docs](/components/llms). - `provider` (String): The provider for the language model, which is set to 'openai'. You can find the full list of llm providers in [our docs](/components/llms).
- `model` (String): The specific model being used, 'gpt-3.5-turbo'.
- `config`: - `config`:
- `model` (String): The specific model being used, 'gpt-3.5-turbo'.
- `temperature` (Float): Controls the randomness of the model's output. A higher value (closer to 1) makes the output more random. - `temperature` (Float): Controls the randomness of the model's output. A higher value (closer to 1) makes the output more random.
- `max_tokens` (Integer): Controls how many tokens are used in the response. - `max_tokens` (Integer): Controls how many tokens are used in the response.
- `top_p` (Float): Controls the diversity of word selection. A higher value (closer to 1) makes word selection more diverse. - `top_p` (Float): Controls the diversity of word selection. A higher value (closer to 1) makes word selection more diverse.
- `stream` (Boolean): Controls if the response is streamed back to the user (set to false). - `stream` (Boolean): Controls if the response is streamed back to the user (set to false).
- `template` (String): A custom template for the prompt that the model uses to generate responses. - `template` (String): A custom template for the prompt that the model uses to generate responses.
- `system_prompt` (String): A system prompt for the model to follow when generating responses, in this case, it's set to the style of William Shakespeare. - `system_prompt` (String): A system prompt for the model to follow when generating responses, in this case, it's set to the style of William Shakespeare.
4. `vectordb` Section: - `stream` (Boolean): Controls if the response is streamed back to the user (set to false).
- `number_documents` (Integer): Number of documents to pull from the vectordb as context, defaults to 1
3. `vectordb` Section:
- `provider` (String): The provider for the vector database, set to 'chroma'. You can find the full list of vector database providers in [our docs](/components/vector-databases). - `provider` (String): The provider for the vector database, set to 'chroma'. You can find the full list of vector database providers in [our docs](/components/vector-databases).
- `config`: - `config`:
- `collection_name` (String): The initial collection name for the database, set to 'full-stack-app'. - `collection_name` (String): The initial collection name for the vectordb, set to 'full-stack-app'.
- `dir` (String): The directory for the database, set to 'db'. - `dir` (String): The directory for the local database, set to 'db'.
- `allow_reset` (Boolean): Indicates whether resetting the database is allowed, set to true. - `allow_reset` (Boolean): Indicates whether resetting the vectordb is allowed, set to true.
5. `embedder` Section: <Note>We recommend you to checkout vectordb specific config [here](https://docs.embedchain.ai/components/vector-databases)</Note>
4. `embedder` Section:
- `provider` (String): The provider for the embedder, set to 'openai'. You can find the full list of embedding model providers in [our docs](/components/embedding-models). - `provider` (String): The provider for the embedder, set to 'openai'. You can find the full list of embedding model providers in [our docs](/components/embedding-models).
- `config`: - `config`:
- `model` (String): The specific model used for text embedding, 'text-embedding-ada-002'. - `model` (String): The specific model used for text embedding, 'text-embedding-ada-002'.
5. `chunker` Section:
- `chunk_size` (Integer): The size of each chunk of text that is sent to the language model.
- `chunk_overlap` (Integer): The amount of overlap between each chunk of text.
- `length_function` (String): The function used to calculate the length of each chunk of text. In this case, it's set to 'len'. You can also use any function import directly as a string here.
If you have questions about the configuration above, please feel free to reach out to us using one of the following methods: If you have questions about the configuration above, please feel free to reach out to us using one of the following methods:
<Snippet file="get-help.mdx" /> <Snippet file="get-help.mdx" />
+7 -7
View File
@@ -29,7 +29,7 @@ from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx' os.environ['OPENAI_API_KEY'] = 'xxx'
# load embedding model configuration from config.yaml file # load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
app.add("https://en.wikipedia.org/wiki/OpenAI") app.add("https://en.wikipedia.org/wiki/OpenAI")
app.query("What is OpenAI?") app.query("What is OpenAI?")
@@ -55,11 +55,11 @@ import os
from embedchain import Pipeline as App from embedchain import Pipeline as App
os.environ["OPENAI_API_TYPE"] = "azure" os.environ["OPENAI_API_TYPE"] = "azure"
os.environ["OPENAI_API_BASE"] = "https://xxx.openai.azure.com/" os.environ["AZURE_OPENAI_ENDPOINT"] = "https://xxx.openai.azure.com/"
os.environ["OPENAI_API_KEY"] = "xxx" os.environ["AZURE_OPENAI_API_KEY"] = "xxx"
os.environ["OPENAI_API_VERSION"] = "xxx" os.environ["OPENAI_API_VERSION"] = "xxx"
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -93,7 +93,7 @@ GPT4All supports generating high quality embeddings of arbitrary length document
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file # load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -122,7 +122,7 @@ Hugging Face supports generating embeddings of arbitrary length documents of tex
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file # load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -153,7 +153,7 @@ Embedchain supports Google's VertexAI embeddings model through a simple interfac
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file # load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
+9 -9
View File
@@ -46,7 +46,7 @@ from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx' os.environ['OPENAI_API_KEY'] = 'xxx'
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -78,7 +78,7 @@ os.environ["OPENAI_API_BASE"] = "https://xxx.openai.azure.com/"
os.environ["OPENAI_API_KEY"] = "xxx" os.environ["OPENAI_API_KEY"] = "xxx"
os.environ["OPENAI_API_VERSION"] = "xxx" os.environ["OPENAI_API_VERSION"] = "xxx"
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -115,7 +115,7 @@ from embedchain import Pipeline as App
os.environ["ANTHROPIC_API_KEY"] = "xxx" os.environ["ANTHROPIC_API_KEY"] = "xxx"
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -152,7 +152,7 @@ from embedchain import Pipeline as App
os.environ["COHERE_API_KEY"] = "xxx" os.environ["COHERE_API_KEY"] = "xxx"
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -183,7 +183,7 @@ GPT4all is a free-to-use, locally running, privacy-aware chatbot. No GPU or inte
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -216,7 +216,7 @@ from embedchain import Pipeline as App
os.environ["JINACHAT_API_KEY"] = "xxx" os.environ["JINACHAT_API_KEY"] = "xxx"
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -253,7 +253,7 @@ from embedchain import Pipeline as App
os.environ["HUGGINGFACE_ACCESS_TOKEN"] = "xxx" os.environ["HUGGINGFACE_ACCESS_TOKEN"] = "xxx"
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -283,7 +283,7 @@ from embedchain import Pipeline as App
os.environ["REPLICATE_API_TOKEN"] = "xxx" os.environ["REPLICATE_API_TOKEN"] = "xxx"
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -308,7 +308,7 @@ Setup Google Cloud Platform application credentials by following the instruction
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load llm configuration from config.yaml file # load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
+12 -9
View File
@@ -25,7 +25,7 @@ Utilizing a vector database alongside Embedchain is a seamless process. All you
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load chroma configuration from yaml file # load chroma configuration from yaml file
app = App.from_config(yaml_path="config1.yaml") app = App.from_config(config_path="config1.yaml")
``` ```
```yaml config1.yaml ```yaml config1.yaml
@@ -64,7 +64,7 @@ pip install --upgrade 'embedchain[elasticsearch]'
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load elasticsearch configuration from yaml file # load elasticsearch configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -73,8 +73,11 @@ vectordb:
config: config:
collection_name: 'es-index' collection_name: 'es-index'
es_url: http://localhost:9200 es_url: http://localhost:9200
allow_reset: true http_auth:
- admin
- admin
api_key: xxx api_key: xxx
verify_certs: false
``` ```
</CodeGroup> </CodeGroup>
@@ -92,19 +95,19 @@ pip install --upgrade 'embedchain[opensearch]'
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load opensearch configuration from yaml file # load opensearch configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
vectordb: vectordb:
provider: opensearch provider: opensearch
config: config:
collection_name: 'my-app'
opensearch_url: 'https://localhost:9200' opensearch_url: 'https://localhost:9200'
http_auth: http_auth:
- admin - admin
- admin - admin
vector_dimension: 1536 vector_dimension: 1536
collection_name: 'my-app'
use_ssl: false use_ssl: false
verify_certs: false verify_certs: false
``` ```
@@ -131,7 +134,7 @@ os.environ['ZILLIZ_CLOUD_URI'] = 'https://xxx.zillizcloud.com'
os.environ['ZILLIZ_CLOUD_TOKEN'] = 'xxx' os.environ['ZILLIZ_CLOUD_TOKEN'] = 'xxx'
# load zilliz configuration from yaml file # load zilliz configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -167,7 +170,7 @@ In order to use Pinecone as vector database, set the environment variables `PINE
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load pinecone configuration from yaml file # load pinecone configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -190,7 +193,7 @@ In order to use Qdrant as a vector database, set the environment variables `QDRA
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load qdrant configuration from yaml file # load qdrant configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
@@ -210,7 +213,7 @@ In order to use Weaviate as a vector database, set the environment variables `WE
from embedchain import Pipeline as App from embedchain import Pipeline as App
# load weaviate configuration from yaml file # load weaviate configuration from yaml file
app = App.from_config(yaml_path="config.yaml") app = App.from_config(config_path="config.yaml")
``` ```
```yaml config.yaml ```yaml config.yaml
+41
View File
@@ -0,0 +1,41 @@
---
title: '⚙️ Custom'
---
When we say "custom", we mean that you can customize the loader and chunker to your needs. This is done by passing a custom loader and chunker to the `add` method.
```python
from embedchain import Pipeline as App
import your_loader
import your_chunker
app = App()
loader = your_loader()
chunker = your_chunker()
app.add("source", data_type="custom", loader=loader, chunker=chunker)
```
<Note>
The custom loader and chunker must be a class that inherits from the [`BaseLoader`](https://github.com/embedchain/embedchain/blob/main/embedchain/loaders/base_loader.py) and [`BaseChunker`](https://github.com/embedchain/embedchain/blob/main/embedchain/chunkers/base_chunker.py) classes respectively.
</Note>
<Note>
If the `data_type` is not a valid data type, the `add` method will fallback to the `custom` data type and expect a custom loader and chunker to be passed by the user.
</Note>
Example:
```python
from embedchain import Pipeline as App
from embedchain.loaders.github import GithubLoader
app = App()
loader = GithubLoader(config={"token": "ghp_xxx"})
app.add("repo:embedchain/embedchain type:repo", data_type="github", loader=loader)
app.query("What is Embedchain?")
# Answer: Embedchain is a Data Platform for Large Language Models (LLMs). It allows users to seamlessly load, index, retrieve, and sync unstructured data in order to build dynamic, LLM-powered applications. There is also a JavaScript implementation called embedchain-js available on GitHub.
```
+12
View File
@@ -50,3 +50,15 @@ from embedchain import Pipeline as App
naval_chat_bot = App() naval_chat_bot = App()
print(naval_chat_bot.query("What unique capacity does Naval argue humans possess when it comes to understanding explanations or concepts?")) print(naval_chat_bot.query("What unique capacity does Naval argue humans possess when it comes to understanding explanations or concepts?"))
``` ```
## Resetting an app and vector database
You can reset the app by simply calling the `reset` method. This will delete the vector database and all other app related files.
```python
from embedchain import Pipeline as App
app = App()
app.add("https://www.youtube.com/watch?v=3qHkcs3kG44")
app.reset()
```
+28
View File
@@ -0,0 +1,28 @@
---
title: "💬 Discord"
---
To add any Discord channel messages to your app, just add the `channel_id` as the source and set the `data_type` to `discord`.
<Note>
This loader requires a Discord bot token with read messages access.
To obtain the token, follow the instructions provided in this tutorial:
<a href="https://www.writebots.com/discord-bot-token/">How to Get a Discord Bot Token?</a>.
</Note>
```python
import os
from embedchain import Pipeline as App
# add your discord "BOT" token
os.environ["DISCORD_TOKEN"] = "xxx"
app = App()
app.add("1177296711023075338", data_type="discord")
response = app.query("What is Joe saying about Elon Musk?")
print(response)
# Answer: Joe is saying "Elon Musk is a genius".
```
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
title: '📚🌐 Code documentation' title: '📚 Code documentation'
--- ---
To add any code documentation website as a loader, use the data_type as `docs_site`. Eg: To add any code documentation website as a loader, use the data_type as `docs_site`. Eg:
+50
View File
@@ -0,0 +1,50 @@
---
title: 📝 Github
---
1. Setup the Github loader by configuring the Github account with username and personal access token (PAT). Check out [this](https://docs.github.com/en/enterprise-server@3.6/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token) link to learn how to create a PAT.
```Python
from embedchain.loaders.github import GithubLoader
loader = GithubLoader(
config={
"token":"ghp_xxxx"
}
)
```
2. Once you setup the loader, you can create an app and load data using the above Github loader
```Python
import os
from embedchain.pipeline import Pipeline as App
os.environ["OPENAI_API_KEY"] = "sk-xxxx"
app = App()
app.add("repo:embedchain/embedchain type:repo", data_type="github", loader=loader)
response = app.query("What is Embedchain?")
# Answer: Embedchain is a Data Platform for Large Language Models (LLMs). It allows users to seamlessly load, index, retrieve, and sync unstructured data in order to build dynamic, LLM-powered applications. There is also a JavaScript implementation called embedchain-js available on GitHub.
```
The `add` function of the app will accept any valid github query with qualifiers. It only supports loading github code, repository, issues and pull-requests.
<Note>
You must provide qualifiers `type:` and `repo:` in the query. The `type:` qualifier can be a combination of `code`, `repo`, `pr`, `issue`. The `repo:` qualifier must be a valid github repository name.
</Note>
<Card title="Valid queries" icon="lightbulb" iconType="duotone" color="#ca8b04">
- `repo:embedchain/embedchain type:repo` - to load the repository
- `repo:embedchain/embedchain type:issue,pr` - to load the issues and pull-requests of the repository
- `repo:embedchain/embedchain type:issue state:closed` - to load the closed issues of the repository
</Card>
3. We automatically create a chunker to chunk your GitHub data, however if you wish to provide your own chunker class. Here is how you can do that:
```Python
from embedchain.chunkers.common_chunker import CommonChunker
from embedchain.config.add_config import ChunkerConfig
github_chunker_config = ChunkerConfig(chunk_size=2000, chunk_overlap=0, length_function=len)
github_chunker = CommonChunker(config=github_chunker_config)
app.add(load_query, data_type="github", loader=loader, chunker=github_chunker)
```
-1
View File
@@ -21,7 +21,6 @@ For more details on how to setup with valid config, check MySQL [documentation](
2. Once you setup the loader, you can create an app and load data using the above MySQL loader 2. Once you setup the loader, you can create an app and load data using the above MySQL loader
```Python ```Python
import os
from embedchain.pipeline import Pipeline as App from embedchain.pipeline import Pipeline as App
app = App() app = App()
+9 -6
View File
@@ -5,25 +5,28 @@ title: Overview
Embedchain comes with built-in support for various data sources. We handle the complexity of loading unstructured data from these data sources, allowing you to easily customize your app through a user-friendly interface. Embedchain comes with built-in support for various data sources. We handle the complexity of loading unstructured data from these data sources, allowing you to easily customize your app through a user-friendly interface.
<CardGroup cols={4}> <CardGroup cols={4}>
<Card title="📊 csv" href="/data-sources/csv"></Card> <Card title="📊 CSV" href="/data-sources/csv"></Card>
<Card title="📃 JSON" href="/data-sources/json"></Card> <Card title="📃 JSON" href="/data-sources/json"></Card>
<Card title="📚🌐 docs site" href="/data-sources/docs-site"></Card> <Card title="📚 docs site" href="/data-sources/docs-site"></Card>
<Card title="📄 docx" href="/data-sources/docx"></Card> <Card title="📄 docx" href="/data-sources/docx"></Card>
<Card title="📝 mdx" href="/data-sources/mdx"></Card> <Card title="📝 mdx" href="/data-sources/mdx"></Card>
<Card title="📓 notion" href="/data-sources/notion"></Card> <Card title="📓 Notion" href="/data-sources/notion"></Card>
<Card title="📰 pdf" href="/data-sources/pdf-file"></Card> <Card title="📰 PDF" href="/data-sources/pdf-file"></Card>
<Card title="❓💬 q&a pair" href="/data-sources/qna"></Card> <Card title="❓💬 q&a pair" href="/data-sources/qna"></Card>
<Card title="🗺️ sitemap" href="/data-sources/sitemap"></Card> <Card title="🗺️ sitemap" href="/data-sources/sitemap"></Card>
<Card title="📝 text" href="/data-sources/text"></Card> <Card title="📝 text" href="/data-sources/text"></Card>
<Card title="🌐📄 web page" href="/data-sources/web-page"></Card> <Card title="🌐 web page" href="/data-sources/web-page"></Card>
<Card title="🧾 xml" href="/data-sources/xml"></Card> <Card title="🧾 xml" href="/data-sources/xml"></Card>
<Card title="🙌 OpenAPI" href="/data-sources/openapi"></Card> <Card title="🙌 OpenAPI" href="/data-sources/openapi"></Card>
<Card title="📺 youtube video" href="/data-sources/youtube-video"></Card> <Card title="📺 Youtube" href="/data-sources/youtube-video"></Card>
<Card title="📬 Gmail" href="/data-sources/gmail"></Card> <Card title="📬 Gmail" href="/data-sources/gmail"></Card>
<Card title="🐘 Postgres" href="/data-sources/postgres"></Card> <Card title="🐘 Postgres" href="/data-sources/postgres"></Card>
<Card title="🐬 MySQL" href="/data-sources/mysql"></Card> <Card title="🐬 MySQL" href="/data-sources/mysql"></Card>
<Card title="🤖 Slack" href="/data-sources/slack"></Card> <Card title="🤖 Slack" href="/data-sources/slack"></Card>
<Card title="🗨️ Discourse" href="/data-sources/discourse"></Card> <Card title="🗨️ Discourse" href="/data-sources/discourse"></Card>
<Card title="💬 Discord" href="/data-sources/discord"></Card>
<Card title="📝 Github" href="/data-sources/github"></Card>
<Card title="⚙️ Custom" href="/data-sources/custom"></Card>
</CardGroup> </CardGroup>
<br/ > <br/ >
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
title: '🌐📄 Web page' title: '🌐 Web page'
--- ---
To add any web page, use the data_type as `web_page`. Eg: To add any web page, use the data_type as `web_page`. Eg:
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
title: '📺 Youtube video' title: '📺 Youtube'
--- ---
+8 -1
View File
@@ -1,8 +1,15 @@
--- ---
title: 🔎 Examples title: 🔎 Examples
description: 'Collection of Google colab notebook and Replit links for users'
--- ---
# Explore awesome apps
Check out the remarkable work accomplished using [Embedchain](https://app.embedchain.ai/custom-gpts/).
## Collection of Google colab notebook and Replit links for users
Get started with Embedchain by trying out the examples below. You can run the examples in your browser using Google Colab or Replit.
<table> <table>
<thead> <thead>
<tr> <tr>
+37 -16
View File
@@ -2,13 +2,36 @@
title: ❓ FAQs title: ❓ FAQs
description: 'Collections of all the frequently asked questions' description: 'Collections of all the frequently asked questions'
--- ---
<AccordionGroup>
#### Does Embedchain support OpenAI's Assistant APIs? <Accordion title="Does Embedchain support OpenAI's Assistant APIs?">
Yes, it does. Please refer to the [OpenAI Assistant docs page](/get-started/openai-assistant). Yes, it does. Please refer to the [OpenAI Assistant docs page](/get-started/openai-assistant).
</Accordion>
<Accordion title="How to use MistralAI language model?">
Use the model provided on huggingface: `mistralai/Mistral-7B-v0.1`
<CodeGroup>
```python main.py
import os
from embedchain import Pipeline as App
#### How to use `gpt-4-turbo` model released on OpenAI DevDay? os.environ["OPENAI_API_KEY"] = "sk-xxx"
os.environ["HUGGINGFACE_ACCESS_TOKEN"] = "hf_your_token"
app = App.from_config("huggingface.yaml")
```
```yaml huggingface.yaml
llm:
provider: huggingface
config:
model: 'mistralai/Mistral-7B-v0.1'
temperature: 0.5
max_tokens: 1000
top_p: 0.5
stream: false
```
</CodeGroup>
</Accordion>
<Accordion title="How to use ChatGPT 4 turbo model released on OpenAI DevDay?">
Use the model `gpt-4-turbo` provided my openai.
<CodeGroup> <CodeGroup>
```python main.py ```python main.py
@@ -18,7 +41,7 @@ from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx' os.environ['OPENAI_API_KEY'] = 'xxx'
# load llm configuration from gpt4_turbo.yaml file # load llm configuration from gpt4_turbo.yaml file
app = App.from_config(yaml_path="gpt4_turbo.yaml") app = App.from_config(config_path="gpt4_turbo.yaml")
``` ```
```yaml gpt4_turbo.yaml ```yaml gpt4_turbo.yaml
@@ -31,12 +54,9 @@ llm:
top_p: 1 top_p: 1
stream: false stream: false
``` ```
</CodeGroup> </CodeGroup>
</Accordion>
<Accordion title="How to use GPT-4 as the LLM model?">
#### How to use GPT-4 as the LLM model?
<CodeGroup> <CodeGroup>
```python main.py ```python main.py
@@ -46,7 +66,7 @@ from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx' os.environ['OPENAI_API_KEY'] = 'xxx'
# load llm configuration from gpt4.yaml file # load llm configuration from gpt4.yaml file
app = App.from_config(yaml_path="gpt4.yaml") app = App.from_config(config_path="gpt4.yaml")
``` ```
```yaml gpt4.yaml ```yaml gpt4.yaml
@@ -61,9 +81,8 @@ llm:
``` ```
</CodeGroup> </CodeGroup>
</Accordion>
#### I don't have OpenAI credits. How can I use some open source model? <Accordion title="I don't have OpenAI credits. How can I use some open source model?">
<CodeGroup> <CodeGroup>
```python main.py ```python main.py
@@ -73,7 +92,7 @@ from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx' os.environ['OPENAI_API_KEY'] = 'xxx'
# load llm configuration from opensource.yaml file # load llm configuration from opensource.yaml file
app = App.from_config(yaml_path="opensource.yaml") app = App.from_config(config_path="opensource.yaml")
``` ```
```yaml opensource.yaml ```yaml opensource.yaml
@@ -93,8 +112,10 @@ embedder:
``` ```
</CodeGroup> </CodeGroup>
#### How to contact support? </Accordion>
</AccordionGroup>
#### Need more help?
If docs aren't sufficient, please feel free to reach out to us using one of the following methods: If docs aren't sufficient, please feel free to reach out to us using one of the following methods:
<Snippet file="get-help.mdx" /> <Snippet file="get-help.mdx" />
+3 -1
View File
@@ -105,7 +105,7 @@ app.deploy()
# ✅ Data of type: web_page, value: https://www.forbes.com/profile/elon-musk added successfully. # ✅ Data of type: web_page, value: https://www.forbes.com/profile/elon-musk added successfully.
``` ```
## 🚀 How it works? ## 🛠️ How it works?
Embedchain abstracts out the following steps from you to easily create LLM powered apps: Embedchain abstracts out the following steps from you to easily create LLM powered apps:
@@ -129,3 +129,5 @@ The process of loading the dataset and querying involves multiple steps, each wi
- How should I find similar documents for a query? Which ranking model should I use? - How should I find similar documents for a query? Which ranking model should I use?
Embedchain takes care of all these nuances and provides a simple interface to create apps on any data. Embedchain takes care of all these nuances and provides a simple interface to create apps on any data.
## [🚀 Get started](https://docs.embedchain.ai/get-started/quickstart)
+63 -58
View File
@@ -12,79 +12,84 @@ pip install embedchain
``` ```
<Tip> <Tip>
Embedchain now supports OpenAI's latest `gpt-4-turbo` model. Checkout the [docs here](/get-started/faq#how-to-use-gpt-4-turbo-model-released-on-openai-devday) on how to use it. Embedchain now supports OpenAI's latest `gpt-4-turbo` model. Checkout the [FAQs](/get-started/faq#how-to-use-gpt-4-turbo-model-released-on-openai-devday).
</Tip> </Tip>
Creating an app involves 3 steps: Creating an app involves 3 steps:
<Steps> <Steps>
<Step title="⚙️ Import app instance"> <Step title="⚙️ Import app instance">
```python ```python
from embedchain import Pipeline as App from embedchain import Pipeline as App
app = App() app = App()
``` ```
<Accordion title="Customize your app by a simple YAML config" icon="gear-complex">
Embedchain provides a wide range of options to customize your app. You can customize the model, data sources, and much more.
Explore the custom configurations [here](https://docs.embedchain.ai/advanced/configuration).
<CodeGroup>
```python yaml_app.py
from embedchain import Pipeline as App
app = App.from_config(config_path="config.yaml")
```
```python json_app.py
from embedchain import Pipeline as App
app = App.from_config(config_path="config.json")
```
```python app.py
from embedchain import Pipeline as App
config = {} # Add your config here
app = App.from_config(config=config)
```
</CodeGroup>
</Accordion>
</Step> </Step>
<Step title="🗃️ Add data sources"> <Step title="🗃️ Add data sources">
```python ```python
# Add different data sources app.add("https://en.wikipedia.org/wiki/Elon_Musk")
app.add("https://en.wikipedia.org/wiki/Elon_Musk") app.add("https://www.forbes.com/profile/elon-musk")
app.add("https://www.forbes.com/profile/elon-musk") # app.add("path/to/file/elon_musk.pdf")
# You can also add local data sources such as pdf, csv files etc. ```
# app.add("/path/to/file.pdf") <Accordion title="Embedchain supports adding data from many data sources." icon="files">
``` Embedchain supports adding data from many data sources including web pages, PDFs, databases, and more.
Explore the list of supported [data sources](https://docs.embedchain.ai/data-sources/overview).
</Accordion>
</Step> </Step>
<Step title="💬 Query or chat or search context on your data"> <Step title="💬 Ask questions, chat, or search through your data with ease">
```python ```python
app.query("What is the net worth of Elon Musk today?") app.query("What is the net worth of Elon Musk today?")
# Answer: The net worth of Elon Musk today is $258.7 billion. # Answer: The net worth of Elon Musk today is $258.7 billion.
``` ```
<Accordion title="Want to chat with your app?" icon="face-thinking">
Embedchain provides a wide range of features to interact with your app. You can chat with your app, ask questions, search through your data, and much more.
```python
app.chat("How many companies does Elon Musk run? Name those")
# Answer: Elon Musk runs 3 companies: Tesla, SpaceX, and Neuralink.
app.chat("What is his net worth today?")
# Answer: The net worth of Elon Musk today is $258.7 billion.
```
To learn about other features, click [here](https://docs.embedchain.ai/get-started/introduction)
</Accordion>
</Step> </Step>
<Step title="🚀 (Optional) Deploy your pipeline to Embedchain Platform"> <Step title="🚀 Seamlessly launch your App on the Embedchain Platform!">
```python ```python
app.deploy() app.deploy()
# 🔑 Enter your Embedchain API key. You can find the API key at https://app.embedchain.ai/settings/keys/ # 🔑 Enter your Embedchain API key. You can find the API key at https://app.embedchain.ai/settings/keys/
# ec-xxxxxx # ec-xxxxxx
# 🛠️ Creating pipeline on the platform... # 🛠️ Creating pipeline on the platform...
# 🎉🎉🎉 Pipeline created successfully! View your pipeline: https://app.embedchain.ai/pipelines/xxxxx # 🎉🎉🎉 Pipeline created successfully! View your pipeline: https://app.embedchain.ai/pipelines/xxxxx
# 🛠️ Adding data to your pipeline... # 🛠️ Adding data to your pipeline...
# ✅ Data of type: web_page, value: https://www.forbes.com/profile/elon-musk added successfully. # ✅ Data of type: web_page, value: https://www.forbes.com/profile/elon-musk added successfully.
``` ```
<Accordion title="Share your app with others" icon="laptop-mobile">
You can now share your app with others from our platform.
Access your app on our [platform](https://app.embedchain.ai/).
</Accordion>
</Step> </Step>
</Steps> </Steps>
Putting it together, you can run your first app using the following code. Make sure to set the `OPENAI_API_KEY` 🔑 environment variable in the code. Putting it together, you can run your first app using the following Google Colab. Make sure to set the `OPENAI_API_KEY` 🔑 environment variable in the code.
```python
import os
from embedchain import Pipeline as App
os.environ["OPENAI_API_KEY"] = "xxx"
app = App()
# Add different data sources
app.add("https://en.wikipedia.org/wiki/Elon_Musk")
app.add("https://www.forbes.com/profile/elon-musk")
# You can also add local data sources such as pdf, csv files etc.
# app.add("/path/to/file.pdf")
response = app.query("What is the net worth of Elon Musk today?")
print(response)
# Answer: The net worth of Elon Musk today is $258.7 billion.
app.deploy()
# 🔑 Enter your Embedchain API key. You can find the API key at https://app.embedchain.ai/settings/keys/
# ec-xxxxxx
# 🛠️ Creating pipeline on the platform...
# 🎉🎉🎉 Pipeline created successfully! View your pipeline: https://app.embedchain.ai/pipelines/xxxxx
# 🛠️ Adding data to your pipeline...
# ✅ Data of type: web_page, value: https://www.forbes.com/profile/elon-musk added successfully.
```
You can try it out yourself using the following Google Colab notebook:
<a href="https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing"> <a href="https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing">
<img src="https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667" alt="Open in Colab" /> <img src="https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667" alt="Open in Colab" />
+2 -1
View File
@@ -89,7 +89,8 @@
"data-sources/openapi", "data-sources/openapi",
"data-sources/youtube-video", "data-sources/youtube-video",
"data-sources/discourse", "data-sources/discourse",
"data-sources/substack" "data-sources/substack",
"data-sources/discord"
] ]
}, },
"data-sources/data-type-handling" "data-sources/data-type-handling"
+3
View File
@@ -6,3 +6,6 @@ from embedchain.apps.app import App # noqa: F401
from embedchain.client import Client # noqa: F401 from embedchain.client import Client # noqa: F401
from embedchain.pipeline import Pipeline # noqa: F401 from embedchain.pipeline import Pipeline # noqa: F401
from embedchain.vectordb.chroma import ChromaDB # noqa: F401 from embedchain.vectordb.chroma import ChromaDB # noqa: F401
# Setup the user directory if doesn't exist already
Client.setup_dir()
+3 -10
View File
@@ -2,7 +2,6 @@ from typing import Optional
import yaml import yaml
from embedchain.client import Client
from embedchain.config import (AppConfig, BaseEmbedderConfig, BaseLlmConfig, from embedchain.config import (AppConfig, BaseEmbedderConfig, BaseLlmConfig,
ChunkerConfig) ChunkerConfig)
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
@@ -10,10 +9,10 @@ from embedchain.embedchain import EmbedChain
from embedchain.embedder.base import BaseEmbedder from embedchain.embedder.base import BaseEmbedder
from embedchain.embedder.openai import OpenAIEmbedder from embedchain.embedder.openai import OpenAIEmbedder
from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
from embedchain.llm.openai import OpenAILlm from embedchain.llm.openai import OpenAILlm
from embedchain.utils import validate_yaml_config from embedchain.utils import validate_config
from embedchain.vectordb.base import BaseVectorDB from embedchain.vectordb.base import BaseVectorDB
from embedchain.vectordb.chroma import ChromaDB from embedchain.vectordb.chroma import ChromaDB
@@ -68,9 +67,6 @@ class App(EmbedChain):
:type system_prompt: Optional[str], optional :type system_prompt: Optional[str], optional
:raises TypeError: LLM, database or embedder or their config is not a valid class instance. :raises TypeError: LLM, database or embedder or their config is not a valid class instance.
""" """
# Setup user directory if it doesn't exist already
Client.setup_dir()
# Type check configs # Type check configs
if config and not isinstance(config, AppConfig): if config and not isinstance(config, AppConfig):
raise TypeError( raise TypeError(
@@ -134,14 +130,11 @@ class App(EmbedChain):
:return: An instance of the App class. :return: An instance of the App class.
:rtype: App :rtype: App
""" """
# Setup user directory if it doesn't exist already
Client.setup_dir()
with open(yaml_path, "r") as file: with open(yaml_path, "r") as file:
config_data = yaml.safe_load(file) config_data = yaml.safe_load(file)
try: try:
validate_yaml_config(config_data) validate_config(config_data)
except Exception as e: except Exception as e:
raise Exception(f"❌ Error occurred while validating the YAML config. Error: {str(e)}") raise Exception(f"❌ Error occurred while validating the YAML config. Error: {str(e)}")
+2 -2
View File
@@ -3,8 +3,8 @@ from typing import Any
from embedchain import Pipeline as App from embedchain import Pipeline as App
from embedchain.config import AddConfig, BaseLlmConfig, PipelineConfig from embedchain.config import AddConfig, BaseLlmConfig, PipelineConfig
from embedchain.embedder.openai import OpenAIEmbedder from embedchain.embedder.openai import OpenAIEmbedder
from embedchain.helper.json_serializable import (JSONSerializable, from embedchain.helpers.json_serializable import (JSONSerializable,
register_deserializable) 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
+1 -1
View File
@@ -2,7 +2,7 @@ import argparse
import logging import logging
import os import os
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .base import BaseBot from .base import BaseBot
+1 -1
View File
@@ -3,7 +3,7 @@ import logging
import os import os
from typing import List, Optional from typing import List, Optional
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .base import BaseBot from .base import BaseBot
+1 -1
View File
@@ -5,7 +5,7 @@ import signal
import sys import sys
from embedchain import App from embedchain import App
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .base import BaseBot from .base import BaseBot
+1 -1
View File
@@ -4,7 +4,7 @@ import logging
import signal import signal
import sys import sys
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .base import BaseBot from .base import BaseBot
+1 -2
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
from embedchain.models.data_type import DataType from embedchain.models.data_type import DataType
@@ -41,7 +41,6 @@ class BaseChunker(JSONSerializable):
url = meta_data["url"] url = meta_data["url"]
chunks = self.get_chunks(content) chunks = self.get_chunks(content)
for chunk in chunks: for chunk in chunks:
chunk_id = hashlib.sha256((chunk + url).encode()).hexdigest() chunk_id = hashlib.sha256((chunk + url).encode()).hexdigest()
chunk_id = f"{app_id}--{chunk_id}" if app_id is not None else chunk_id chunk_id = f"{app_id}--{chunk_id}" if app_id is not None else chunk_id
+2 -2
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
@@ -13,7 +13,7 @@ class CommonChunker(BaseChunker):
def __init__(self, config: Optional[ChunkerConfig] = None): def __init__(self, config: Optional[ChunkerConfig] = None):
if config is None: if config is None:
config = ChunkerConfig(chunk_size=1000, chunk_overlap=0, length_function=len) config = ChunkerConfig(chunk_size=2000, chunk_overlap=0, length_function=len)
text_splitter = RecursiveCharacterTextSplitter( text_splitter = RecursiveCharacterTextSplitter(
chunk_size=config.chunk_size, chunk_size=config.chunk_size,
chunk_overlap=config.chunk_overlap, chunk_overlap=config.chunk_overlap,
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -3,7 +3,7 @@ from importlib import import_module
from typing import Callable, Optional from typing import Callable, Optional
from embedchain.config.base_config import BaseConfig from embedchain.config.base_config import BaseConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .base_app_config import BaseAppConfig from .base_app_config import BaseAppConfig
+1 -1
View File
@@ -2,7 +2,7 @@ import logging
from typing import Optional from typing import Optional
from embedchain.config.base_config import BaseConfig from embedchain.config.base_config import BaseConfig
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
from embedchain.vectordb.base import BaseVectorDB from embedchain.vectordb.base import BaseVectorDB
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Any, Dict from typing import Any, Dict
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
class BaseConfig(JSONSerializable): class BaseConfig(JSONSerializable):
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+6 -2
View File
@@ -1,9 +1,9 @@
import re import re
from string import Template from string import Template
from typing import Any, Dict, Optional from typing import Any, Dict, List, Optional
from embedchain.config.base_config import BaseConfig from embedchain.config.base_config import BaseConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
DEFAULT_PROMPT = """ DEFAULT_PROMPT = """
Use the following pieces of context to answer the query at the end. Use the following pieces of context to answer the query at the end.
@@ -68,6 +68,7 @@ class BaseLlmConfig(BaseConfig):
system_prompt: Optional[str] = None, system_prompt: Optional[str] = None,
where: Dict[str, Any] = None, where: Dict[str, Any] = None,
query_type: Optional[str] = None, query_type: Optional[str] = None,
callbacks: Optional[List] = None,
): ):
""" """
Initializes a configuration class instance for the LLM. Initializes a configuration class instance for the LLM.
@@ -98,6 +99,8 @@ class BaseLlmConfig(BaseConfig):
:type system_prompt: Optional[str], optional :type system_prompt: Optional[str], optional
:param where: A dictionary of key-value pairs to filter the database results., defaults to None :param where: A dictionary of key-value pairs to filter the database results., defaults to None
:type where: Dict[str, Any], optional :type where: Dict[str, Any], optional
:param callbacks: Langchain callback functions to use, defaults to None
:type callbacks: Optional[List], optional
:raises ValueError: If the template is not valid as template should :raises ValueError: If the template is not valid as template should
contain $context and $query (and optionally $history) contain $context and $query (and optionally $history)
:raises ValueError: Stream is not boolean :raises ValueError: Stream is not boolean
@@ -113,6 +116,7 @@ class BaseLlmConfig(BaseConfig):
self.deployment_name = deployment_name self.deployment_name = deployment_name
self.system_prompt = system_prompt self.system_prompt = system_prompt
self.query_type = query_type self.query_type = query_type
self.callbacks = callbacks
if type(template) is str: if type(template) is str:
template = Template(template) template = Template(template)
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from .apps.base_app_config import BaseAppConfig from .apps.base_app_config import BaseAppConfig
+1 -1
View File
@@ -1,7 +1,7 @@
from typing import Optional from typing import Optional
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -2,7 +2,7 @@ import os
from typing import Dict, List, Optional, Union from typing import Dict, List, Optional, Union
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -1,7 +1,7 @@
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -1,7 +1,7 @@
from typing import Dict, Optional from typing import Dict, Optional
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -1,7 +1,7 @@
from typing import Dict, Optional from typing import Dict, Optional
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -1,7 +1,7 @@
from typing import Dict, Optional from typing import Dict, Optional
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+1 -1
View File
@@ -2,7 +2,7 @@ import os
from typing import Optional from typing import Optional
from embedchain.config.vectordb.base import BaseVectorDbConfig from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
@register_deserializable @register_deserializable
+31 -39
View File
@@ -1,10 +1,10 @@
from importlib import import_module from importlib import import_module
from typing import Any, Dict from typing import Optional
from embedchain.chunkers.base_chunker import BaseChunker from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config import AddConfig from embedchain.config import AddConfig
from embedchain.config.add_config import ChunkerConfig, LoaderConfig from embedchain.config.add_config import ChunkerConfig, LoaderConfig
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.models.data_type import DataType from embedchain.models.data_type import DataType
@@ -16,7 +16,13 @@ class DataFormatter(JSONSerializable):
.add or .add_local method call .add or .add_local method call
""" """
def __init__(self, data_type: DataType, config: AddConfig, kwargs: Dict[str, Any]): def __init__(
self,
data_type: DataType,
config: AddConfig,
loader: Optional[BaseLoader] = None,
chunker: Optional[BaseChunker] = None,
):
""" """
Initialize a dataformatter, set data type and chunker based on datatype. Initialize a dataformatter, set data type and chunker based on datatype.
@@ -25,15 +31,15 @@ class DataFormatter(JSONSerializable):
:param config: AddConfig instance with nested loader and chunker config attributes. :param config: AddConfig instance with nested loader and chunker config attributes.
:type config: AddConfig :type config: AddConfig
""" """
self.loader = self._get_loader(data_type=data_type, config=config.loader, kwargs=kwargs) self.loader = self._get_loader(data_type=data_type, config=config.loader, loader=loader)
self.chunker = self._get_chunker(data_type=data_type, config=config.chunker, kwargs=kwargs) self.chunker = self._get_chunker(data_type=data_type, config=config.chunker, chunker=chunker)
def _lazy_load(self, module_path: str): def _lazy_load(self, module_path: str):
module_path, class_name = module_path.rsplit(".", 1) module_path, class_name = module_path.rsplit(".", 1)
module = import_module(module_path) module = import_module(module_path)
return getattr(module, class_name) return getattr(module, class_name)
def _get_loader(self, data_type: DataType, config: LoaderConfig, kwargs: Dict[str, Any]) -> BaseLoader: def _get_loader(self, data_type: DataType, config: LoaderConfig, loader: Optional[BaseLoader]) -> BaseLoader:
""" """
Returns the appropriate data loader for the given data type. Returns the appropriate data loader for the given data type.
@@ -64,26 +70,17 @@ class DataFormatter(JSONSerializable):
DataType.GMAIL: "embedchain.loaders.gmail.GmailLoader", DataType.GMAIL: "embedchain.loaders.gmail.GmailLoader",
DataType.NOTION: "embedchain.loaders.notion.NotionLoader", DataType.NOTION: "embedchain.loaders.notion.NotionLoader",
DataType.SUBSTACK: "embedchain.loaders.substack.SubstackLoader", DataType.SUBSTACK: "embedchain.loaders.substack.SubstackLoader",
DataType.GITHUB: "embedchain.loaders.github.GithubLoader",
DataType.YOUTUBE_CHANNEL: "embedchain.loaders.youtube_channel.YoutubeChannelLoader", DataType.YOUTUBE_CHANNEL: "embedchain.loaders.youtube_channel.YoutubeChannelLoader",
DataType.DISCORD: "embedchain.loaders.discord.DiscordLoader",
} }
custom_loaders = set( if data_type == DataType.CUSTOM or loader is not None:
[ loader_class: type = loader
DataType.POSTGRES, if loader_class:
DataType.MYSQL, return loader_class
DataType.SLACK, elif data_type in loaders:
DataType.DISCOURSE,
]
)
if data_type in loaders:
loader_class: type = self._lazy_load(loaders[data_type]) loader_class: type = self._lazy_load(loaders[data_type])
return loader_class() return loader_class()
elif data_type in custom_loaders:
loader_class: type = kwargs.get("loader", None)
if loader_class is not None:
return loader_class
raise ValueError( raise ValueError(
f"Cant find the loader for {data_type}.\ f"Cant find the loader for {data_type}.\
@@ -91,7 +88,7 @@ class DataFormatter(JSONSerializable):
check `https://docs.embedchain.ai/data-sources/overview`." check `https://docs.embedchain.ai/data-sources/overview`."
) )
def _get_chunker(self, data_type: DataType, config: ChunkerConfig, kwargs: Dict[str, Any]) -> BaseChunker: def _get_chunker(self, data_type: DataType, config: ChunkerConfig, chunker: Optional[BaseChunker]) -> BaseChunker:
"""Returns the appropriate chunker for the given data type (updated for lazy loading).""" """Returns the appropriate chunker for the given data type (updated for lazy loading)."""
chunker_classes = { chunker_classes = {
DataType.YOUTUBE_VIDEO: "embedchain.chunkers.youtube_video.YoutubeVideoChunker", DataType.YOUTUBE_VIDEO: "embedchain.chunkers.youtube_video.YoutubeVideoChunker",
@@ -111,27 +108,22 @@ class DataFormatter(JSONSerializable):
DataType.OPENAPI: "embedchain.chunkers.openapi.OpenAPIChunker", DataType.OPENAPI: "embedchain.chunkers.openapi.OpenAPIChunker",
DataType.GMAIL: "embedchain.chunkers.gmail.GmailChunker", DataType.GMAIL: "embedchain.chunkers.gmail.GmailChunker",
DataType.NOTION: "embedchain.chunkers.notion.NotionChunker", DataType.NOTION: "embedchain.chunkers.notion.NotionChunker",
DataType.POSTGRES: "embedchain.chunkers.postgres.PostgresChunker",
DataType.MYSQL: "embedchain.chunkers.mysql.MySQLChunker",
DataType.SLACK: "embedchain.chunkers.slack.SlackChunker",
DataType.DISCOURSE: "embedchain.chunkers.discourse.DiscourseChunker",
DataType.SUBSTACK: "embedchain.chunkers.substack.SubstackChunker", DataType.SUBSTACK: "embedchain.chunkers.substack.SubstackChunker",
DataType.GITHUB: "embedchain.chunkers.common_chunker.CommonChunker",
DataType.YOUTUBE_CHANNEL: "embedchain.chunkers.common_chunker.CommonChunker", DataType.YOUTUBE_CHANNEL: "embedchain.chunkers.common_chunker.CommonChunker",
DataType.DISCORD: "embedchain.chunkers.common_chunker.CommonChunker",
DataType.CUSTOM: "embedchain.chunkers.common_chunker.CommonChunker",
} }
if data_type in chunker_classes: if chunker is not None:
if "chunker" in kwargs: return chunker
chunker_class = kwargs.get("chunker") elif data_type in chunker_classes:
else: chunker_class = self._lazy_load(chunker_classes[data_type])
chunker_class = self._lazy_load(chunker_classes[data_type])
chunker = chunker_class(config) chunker = chunker_class(config)
chunker.set_data_type(data_type) chunker.set_data_type(data_type)
return chunker return chunker
else:
raise ValueError( raise ValueError(
f"Cant find the chunker for {data_type}.\ f"Cant find the chunker for {data_type}.\
We recommend to pass the chunker to use data_type: {data_type},\ We recommend to pass the chunker to use data_type: {data_type},\
check `https://docs.embedchain.ai/data-sources/overview`." check `https://docs.embedchain.ai/data-sources/overview`."
) )
+27 -25
View File
@@ -13,7 +13,7 @@ from embedchain.config.apps.base_app_config import BaseAppConfig
from embedchain.constants import SQLITE_PATH from embedchain.constants import SQLITE_PATH
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.helpers.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, from embedchain.models.data_type import (DataType, DirectDataType,
@@ -133,7 +133,9 @@ class EmbedChain(JSONSerializable):
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None,
config: Optional[AddConfig] = None, config: Optional[AddConfig] = None,
dry_run=False, dry_run=False,
**kwargs: Dict[str, Any], loader: Optional[BaseLoader] = None,
chunker: Optional[BaseChunker] = None,
**kwargs: Optional[Dict[str, Any]],
): ):
""" """
Adds the data from the given URL to the vector db. Adds the data from the given URL to the vector db.
@@ -178,10 +180,10 @@ class EmbedChain(JSONSerializable):
try: try:
data_type = DataType(data_type) data_type = DataType(data_type)
except ValueError: except ValueError:
raise ValueError( logging.info(
f"Invalid data_type: '{data_type}'.", f"Invalid data_type: '{data_type}', using `custom` instead.\n Check docs to pass the valid data type: `https://docs.embedchain.ai/data-sources/overview`" # noqa: E501
f"Please use one of the following: {[data_type.value for data_type in DataType]}", )
) from None data_type = DataType.CUSTOM
if not data_type: if not data_type:
data_type = detect_datatype(source) data_type = detect_datatype(source)
@@ -190,21 +192,11 @@ class EmbedChain(JSONSerializable):
hash_object = hashlib.md5(str(source).encode("utf-8")) hash_object = hashlib.md5(str(source).encode("utf-8"))
source_hash = hash_object.hexdigest() source_hash = hash_object.hexdigest()
# Check if the data hash already exists, if so, skip the addition
self.cursor.execute(
"SELECT 1 FROM data_sources WHERE hash = ? AND pipeline_id = ?", (source_hash, self.config.id)
)
existing_data = self.cursor.fetchone()
if existing_data:
print(f"Data with hash {source_hash} already exists. Skipping addition.")
return source_hash
self.user_asks.append([source, data_type.value, metadata]) self.user_asks.append([source, data_type.value, metadata])
data_formatter = DataFormatter(data_type, config, kwargs) data_formatter = DataFormatter(data_type, config, loader, chunker)
documents, metadatas, _ids, new_chunks = self._load_and_embed( documents, metadatas, _ids, new_chunks = self._load_and_embed(
data_formatter.loader, data_formatter.chunker, source, metadata, source_hash, dry_run data_formatter.loader, data_formatter.chunker, source, metadata, source_hash, dry_run, **kwargs
) )
if data_type in {DataType.DOCS_SITE}: if data_type in {DataType.DOCS_SITE}:
self.is_docs_site_instance = True self.is_docs_site_instance = True
@@ -212,7 +204,7 @@ class EmbedChain(JSONSerializable):
# Insert the data into the 'data' table # Insert the data into the 'data' table
self.cursor.execute( self.cursor.execute(
""" """
INSERT INTO data_sources (hash, pipeline_id, type, value, metadata) INSERT OR REPLACE INTO data_sources (hash, pipeline_id, type, value, metadata)
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?)
""", """,
(source_hash, self.config.id, data_type.value, str(source), json.dumps(metadata)), (source_hash, self.config.id, data_type.value, str(source), json.dumps(metadata)),
@@ -248,7 +240,7 @@ class EmbedChain(JSONSerializable):
data_type: Optional[DataType] = None, data_type: Optional[DataType] = None,
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None,
config: Optional[AddConfig] = None, config: Optional[AddConfig] = None,
**kwargs: Dict[str, Any], **kwargs: Optional[Dict[str, Any]],
): ):
""" """
Adds the data from the given URL to the vector db. Adds the data from the given URL to the vector db.
@@ -279,7 +271,7 @@ class EmbedChain(JSONSerializable):
data_type=data_type, data_type=data_type,
metadata=metadata, metadata=metadata,
config=config, config=config,
kwargs=kwargs, **kwargs,
) )
def _get_existing_doc_id(self, chunker: BaseChunker, src: Any): def _get_existing_doc_id(self, chunker: BaseChunker, src: Any):
@@ -348,6 +340,7 @@ class EmbedChain(JSONSerializable):
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None,
source_hash: Optional[str] = None, source_hash: Optional[str] = None,
dry_run=False, dry_run=False,
**kwargs: Optional[Dict[str, Any]],
): ):
""" """
Loads the data from the given URL, chunks it, and adds it to database. Loads the data from the given URL, chunks it, and adds it to database.
@@ -441,6 +434,7 @@ class EmbedChain(JSONSerializable):
metadatas=metadatas, metadatas=metadatas,
ids=ids, ids=ids,
skip_embedding=(chunker.data_type == DataType.IMAGES), skip_embedding=(chunker.data_type == DataType.IMAGES),
**kwargs,
) )
count_new_chunks = self.db.count() - chunks_before_addition count_new_chunks = self.db.count() - chunks_before_addition
@@ -458,7 +452,12 @@ class EmbedChain(JSONSerializable):
] ]
def _retrieve_from_database( def _retrieve_from_database(
self, input_query: str, config: Optional[BaseLlmConfig] = None, where=None, citations: bool = False self,
input_query: str,
config: Optional[BaseLlmConfig] = None,
where=None,
citations: bool = False,
**kwargs: Optional[Dict[str, Any]],
) -> Union[List[Tuple[str, str, str]], List[str]]: ) -> Union[List[Tuple[str, str, str]], List[str]]:
""" """
Queries the vector database based on the given input query. Queries the vector database based on the given input query.
@@ -502,6 +501,7 @@ class EmbedChain(JSONSerializable):
where=where, where=where,
skip_embedding=(hasattr(config, "query_type") and config.query_type == "Images"), skip_embedding=(hasattr(config, "query_type") and config.query_type == "Images"),
citations=citations, citations=citations,
**kwargs,
) )
return contexts return contexts
@@ -537,8 +537,9 @@ class EmbedChain(JSONSerializable):
:rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]] :rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]]
""" """
citations = kwargs.get("citations", False) citations = kwargs.get("citations", False)
db_kwargs = {key: value for key, value in kwargs.items() if key != "citations"}
contexts = self._retrieve_from_database( contexts = self._retrieve_from_database(
input_query=input_query, config=config, where=where, citations=citations input_query=input_query, config=config, where=where, citations=citations, **db_kwargs
) )
if citations and len(contexts) > 0 and isinstance(contexts[0], tuple): if citations and len(contexts) > 0 and isinstance(contexts[0], tuple):
contexts_data_for_llm_query = list(map(lambda x: x[0], contexts)) contexts_data_for_llm_query = list(map(lambda x: x[0], contexts))
@@ -564,7 +565,7 @@ class EmbedChain(JSONSerializable):
dry_run=False, dry_run=False,
where: Optional[Dict[str, str]] = None, where: Optional[Dict[str, str]] = None,
**kwargs: Dict[str, Any], **kwargs: Dict[str, Any],
) -> str: ) -> Union[Tuple[str, List[Tuple[str, str, str]]], str]:
""" """
Queries the vector database on the given input query. Queries the vector database on the given input query.
Gets relevant doc based on the query and then passes it to an Gets relevant doc based on the query and then passes it to an
@@ -590,8 +591,9 @@ class EmbedChain(JSONSerializable):
:rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]] :rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]]
""" """
citations = kwargs.get("citations", False) citations = kwargs.get("citations", False)
db_kwargs = {key: value for key, value in kwargs.items() if key != "citations"}
contexts = self._retrieve_from_database( contexts = self._retrieve_from_database(
input_query=input_query, config=config, where=where, citations=citations input_query=input_query, config=config, where=where, citations=citations, **db_kwargs
) )
if citations and len(contexts) > 0 and isinstance(contexts[0], tuple): if citations and len(contexts) > 0 and isinstance(contexts[0], tuple):
contexts_data_for_llm_query = list(map(lambda x: x[0], contexts)) contexts_data_for_llm_query = list(map(lambda x: x[0], contexts))
+11 -6
View File
@@ -3,12 +3,20 @@ from typing import Any, Callable, Optional
from embedchain.config.embedder.base import BaseEmbedderConfig from embedchain.config.embedder.base import BaseEmbedderConfig
try: try:
from chromadb.api.types import Documents, Embeddings from chromadb.api.types import Embeddable, EmbeddingFunction, Embeddings
except RuntimeError: except RuntimeError:
from embedchain.utils import use_pysqlite3 from embedchain.utils import use_pysqlite3
use_pysqlite3() use_pysqlite3()
from chromadb.api.types import Documents, Embeddings from chromadb.api.types import Embeddable, EmbeddingFunction, Embeddings
class EmbeddingFunc(EmbeddingFunction):
def __init__(self, embedding_fn: Callable[[list[str]], list[str]]):
self.embedding_fn = embedding_fn
def __call__(self, input: Embeddable) -> Embeddings:
return self.embedding_fn(input)
class BaseEmbedder: class BaseEmbedder:
@@ -66,7 +74,4 @@ class BaseEmbedder:
:rtype: Callable :rtype: Callable
""" """
def embed_function(texts: Documents) -> Embeddings: return EmbeddingFunc(embeddings.embed_documents)
return embeddings.embed_documents(texts)
return embed_function
+2 -2
View File
@@ -2,7 +2,7 @@ import os
from typing import Optional from typing import Optional
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
from langchain.embeddings import OpenAIEmbeddings from langchain.embeddings import AzureOpenAIEmbeddings
from embedchain.config import BaseEmbedderConfig from embedchain.config import BaseEmbedderConfig
from embedchain.embedder.base import BaseEmbedder from embedchain.embedder.base import BaseEmbedder
@@ -17,7 +17,7 @@ class OpenAIEmbedder(BaseEmbedder):
self.config.model = "text-embedding-ada-002" self.config.model = "text-embedding-ada-002"
if self.config.deployment_name: if self.config.deployment_name:
embeddings = OpenAIEmbeddings(deployment=self.config.deployment_name) embeddings = AzureOpenAIEmbeddings(deployment=self.config.deployment_name)
embedding_fn = BaseEmbedder._langchain_default_concept(embeddings) embedding_fn = BaseEmbedder._langchain_default_concept(embeddings)
else: else:
if os.getenv("OPENAI_API_KEY") is None and os.getenv("OPENAI_ORGANIZATION") is None: if os.getenv("OPENAI_API_KEY") is None and os.getenv("OPENAI_ORGANIZATION") is None:
View File
+73
View File
@@ -0,0 +1,73 @@
import queue
from typing import Any, Dict, List, Union
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.schema import LLMResult
STOP_ITEM = "[END]"
"""
This is a special item that is used to signal the end of the stream.
"""
class StreamingStdOutCallbackHandlerYield(StreamingStdOutCallbackHandler):
"""
This is a callback handler that yields the tokens as they are generated.
For a usage example, see the :func:`generate` function below.
"""
q: queue.Queue
"""
The queue to write the tokens to as they are generated.
"""
def __init__(self, q: queue.Queue) -> None:
"""
Initialize the callback handler.
q: The queue to write the tokens to as they are generated.
"""
super().__init__()
self.q = q
def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None:
"""Run when LLM starts running."""
with self.q.mutex:
self.q.queue.clear()
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
"""Run on new LLM token. Only available when streaming is enabled."""
self.q.put(token)
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
"""Run when LLM ends running."""
self.q.put(STOP_ITEM)
def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> None:
"""Run when LLM errors."""
self.q.put("%s: %s" % (type(error).__name__, str(error)))
self.q.put(STOP_ITEM)
def generate(rq: queue.Queue):
"""
This is a generator that yields the items in the queue until it reaches the stop item.
Usage example:
```
def askQuestion(callback_fn: StreamingStdOutCallbackHandlerYield):
llm = OpenAI(streaming=True, callbacks=[callback_fn])
return llm(prompt="Write a poem about a tree.")
@app.route("/", methods=["GET"])
def generate_output():
q = Queue()
callback_fn = StreamingStdOutCallbackHandlerYield(q)
threading.Thread(target=askQuestion, args=(callback_fn,)).start()
return Response(generate(q), mimetype="text/event-stream")
```
"""
while True:
result: str = rq.get()
if result == STOP_ITEM or result is None:
break
yield result
+1 -1
View File
@@ -3,7 +3,7 @@ import os
from typing import Optional from typing import Optional
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+1 -1
View File
@@ -2,7 +2,7 @@ import logging
from typing import Optional from typing import Optional
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+1 -1
View File
@@ -7,7 +7,7 @@ from embedchain.config import BaseLlmConfig
from embedchain.config.llm.base import (DEFAULT_PROMPT, from embedchain.config.llm.base import (DEFAULT_PROMPT,
DEFAULT_PROMPT_WITH_HISTORY_TEMPLATE, DEFAULT_PROMPT_WITH_HISTORY_TEMPLATE,
DOCS_SITE_PROMPT_TEMPLATE) DOCS_SITE_PROMPT_TEMPLATE)
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
from embedchain.memory.base import ECChatMemory from embedchain.memory.base import ECChatMemory
from embedchain.memory.message import ChatMessage from embedchain.memory.message import ChatMessage
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Optional
from langchain.llms import Cohere from langchain.llms import Cohere
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+1 -1
View File
@@ -4,7 +4,7 @@ from langchain.callbacks.stdout import StdOutCallbackHandler
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+5 -2
View File
@@ -1,11 +1,12 @@
import importlib import importlib
import logging
import os import os
from typing import Optional from typing import Optional
from langchain.llms import HuggingFaceHub from langchain.llms import HuggingFaceHub
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
@@ -42,9 +43,11 @@ class HuggingFaceLlm(BaseLlm):
else: else:
raise ValueError("`top_p` must be > 0.0 and < 1.0") raise ValueError("`top_p` must be > 0.0 and < 1.0")
model = config.model or "google/flan-t5-xxl"
logging.info(f"Using HuggingFaceHub with model {model}")
llm = HuggingFaceHub( llm = HuggingFaceHub(
huggingfacehub_api_token=os.environ["HUGGINGFACE_ACCESS_TOKEN"], huggingfacehub_api_token=os.environ["HUGGINGFACE_ACCESS_TOKEN"],
repo_id=config.model or "google/flan-t5-xxl", repo_id=model,
model_kwargs=model_kwargs, model_kwargs=model_kwargs,
) )
+1 -1
View File
@@ -5,7 +5,7 @@ from langchain.chat_models import JinaChat
from langchain.schema import HumanMessage, SystemMessage 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.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Optional
from langchain.llms import Replicate from langchain.llms import Replicate
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+3 -2
View File
@@ -4,7 +4,7 @@ from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage 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.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
@@ -34,7 +34,8 @@ class OpenAILlm(BaseLlm):
from langchain.callbacks.streaming_stdout import \ from langchain.callbacks.streaming_stdout import \
StreamingStdOutCallbackHandler StreamingStdOutCallbackHandler
chat = ChatOpenAI(**kwargs, streaming=config.stream, callbacks=[StreamingStdOutCallbackHandler()]) callbacks = config.callbacks if config.callbacks else [StreamingStdOutCallbackHandler()]
chat = ChatOpenAI(**kwargs, streaming=config.stream, callbacks=callbacks)
else: else:
chat = ChatOpenAI(**kwargs) chat = ChatOpenAI(**kwargs)
return chat(messages).content return chat(messages).content
+1 -1
View File
@@ -3,7 +3,7 @@ import logging
from typing import Optional from typing import Optional
from embedchain.config import BaseLlmConfig from embedchain.config import BaseLlmConfig
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
+1 -1
View File
@@ -1,4 +1,4 @@
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
class BaseLoader(JSONSerializable): class BaseLoader(JSONSerializable):
+150
View File
@@ -0,0 +1,150 @@
import hashlib
import logging
import os
from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader
@register_deserializable
class DiscordLoader(BaseLoader):
"""
Load data from a Discord Channel ID.
"""
def __init__(self):
if not os.environ.get("DISCORD_TOKEN"):
raise ValueError("DISCORD_TOKEN is not set")
self.token = os.environ.get("DISCORD_TOKEN")
@staticmethod
def _format_message(message):
return {
"message_id": message.id,
"content": message.content,
"author": {
"id": message.author.id,
"name": message.author.name,
"discriminator": message.author.discriminator,
},
"created_at": message.created_at.isoformat(),
"attachments": [
{
"id": attachment.id,
"filename": attachment.filename,
"size": attachment.size,
"url": attachment.url,
"proxy_url": attachment.proxy_url,
"height": attachment.height,
"width": attachment.width,
}
for attachment in message.attachments
],
"embeds": [
{
"title": embed.title,
"type": embed.type,
"description": embed.description,
"url": embed.url,
"timestamp": embed.timestamp.isoformat(),
"color": embed.color,
"footer": {
"text": embed.footer.text,
"icon_url": embed.footer.icon_url,
"proxy_icon_url": embed.footer.proxy_icon_url,
},
"image": {
"url": embed.image.url,
"proxy_url": embed.image.proxy_url,
"height": embed.image.height,
"width": embed.image.width,
},
"thumbnail": {
"url": embed.thumbnail.url,
"proxy_url": embed.thumbnail.proxy_url,
"height": embed.thumbnail.height,
"width": embed.thumbnail.width,
},
"video": {
"url": embed.video.url,
"height": embed.video.height,
"width": embed.video.width,
},
"provider": {
"name": embed.provider.name,
"url": embed.provider.url,
},
"author": {
"name": embed.author.name,
"url": embed.author.url,
"icon_url": embed.author.icon_url,
"proxy_icon_url": embed.author.proxy_icon_url,
},
"fields": [
{
"name": field.name,
"value": field.value,
"inline": field.inline,
}
for field in embed.fields
],
}
for embed in message.embeds
],
}
def load_data(self, channel_id: str):
"""Load data from a Discord Channel ID."""
import discord
messages = []
class DiscordClient(discord.Client):
async def on_ready(self) -> None:
logging.info("Logged on as {0}!".format(self.user))
try:
channel = self.get_channel(int(channel_id))
if not isinstance(channel, discord.TextChannel):
raise ValueError(
f"Channel {channel_id} is not a text channel. " "Only text channels are supported for now."
)
threads = {}
for thread in channel.threads:
threads[thread.id] = thread
async for message in channel.history(limit=None):
messages.append(DiscordLoader._format_message(message))
if message.id in threads:
async for thread_message in threads[message.id].history(limit=None):
messages.append(DiscordLoader._format_message(thread_message))
except Exception as e:
logging.error(e)
await self.close()
finally:
await self.close()
intents = discord.Intents.default()
intents.message_content = True
client = DiscordClient(intents=intents)
client.run(self.token)
meta_data = {
"url": channel_id,
}
messages = str(messages)
doc_id = hashlib.sha256((messages + channel_id).encode()).hexdigest()
return {
"doc_id": doc_id,
"data": [
{
"content": messages,
"meta_data": meta_data,
}
],
}
+1 -1
View File
@@ -12,7 +12,7 @@ except ImportError:
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
+1 -1
View File
@@ -6,7 +6,7 @@ except ImportError:
raise ImportError( raise ImportError(
'Docx file requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'Docx file requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
+263 -83
View File
@@ -2,116 +2,296 @@ import concurrent.futures
import hashlib import hashlib
import logging import logging
import os import os
import re
import shlex
from typing import Any, Dict, Optional
from tqdm import tqdm from tqdm import tqdm
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.loaders.json import JSONLoader from embedchain.utils import clean_string
from embedchain.loaders.mdx import MdxLoader
from embedchain.utils import detect_datatype
GITHUB_URL = "https://github.com"
GITHUB_API_URL = "https://api.github.com"
def _load_file_data(path): VALID_SEARCH_TYPES = set(["code", "repo", "pr", "issue", "discussion"])
data = []
data_content = []
try:
with open(path, "rb") as f:
content = f.read().decode("utf-8")
except Exception as e:
print(f"Error reading file {path}: {e}")
raise ValueError(f"Failed to read file {path}")
meta_data = {}
meta_data["url"] = path
data.append(
{
"content": content,
"meta_data": meta_data,
}
)
data_content.append(content)
doc_id = hashlib.sha256((" ".join(data_content) + path).encode()).hexdigest()
return {
"doc_id": doc_id,
"data": data,
}
class GithubLoader(BaseLoader): class GithubLoader(BaseLoader):
def load_data(self, repo_url): """Load data from github search query."""
"""Load data from a git repo."""
def __init__(self, config: Optional[Dict[str, Any]] = None):
super().__init__()
if not config:
raise ValueError(
"GithubLoader requires a personal access token to use github api. Check - `https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic`" # noqa: E501
)
try: try:
from git import Repo from github import Github
except ImportError as e: except ImportError as e:
raise ValueError( raise ValueError(
"GithubLoader requires extra dependencies. Install with `pip install --upgrade 'embedchain[git]'`" "GithubLoader requires extra dependencies. Install with `pip install --upgrade 'embedchain[github]'`"
) from e ) from e
mdx_loader = MdxLoader() self.config = config
json_loader = JSONLoader() token = config.get("token")
if not token:
raise ValueError(
"GithubLoader requires a personal access token to use github api. Check - `https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic`" # noqa: E501
)
try:
self.client = Github(token)
except Exception as e:
logging.error(f"GithubLoader failed to initialize client: {e}")
self.client = None
def _github_search_code(self, query: str):
"""Search github code."""
data = [] data = []
data_urls = [] results = self.client.search_code(query)
for result in tqdm(results, total=results.totalCount, desc="Loading code files from github"):
url = result.html_url
logging.info(f"Added data from url: {url}")
content = result.decoded_content.decode("utf-8")
metadata = {
"url": url,
}
data.append(
{
"content": clean_string(content),
"meta_data": metadata,
}
)
return data
def _get_github_repo_data(self, repo_url: str):
local_hash = hashlib.sha256(repo_url.encode()).hexdigest()
local_path = f"/tmp/{local_hash}"
data = []
def _get_repo_tree(repo_url: str, local_path: str):
try:
from git import Repo
except ImportError as e:
raise ValueError(
"GithubLoader requires extra dependencies. Install with `pip install --upgrade 'embedchain[github]'`" # noqa: E501
) from e
def _fetch_or_clone_repo(repo_url: str, local_path: str):
if os.path.exists(local_path): if os.path.exists(local_path):
logging.info("Repository already exists. Fetching updates...") logging.info("Repository already exists. Fetching updates...")
repo = Repo(local_path) repo = Repo(local_path)
origin = repo.remotes.origin
origin.fetch()
logging.info("Fetch completed.") logging.info("Fetch completed.")
else: else:
logging.info("Cloning repository...") logging.info("Cloning repository...")
Repo.clone_from(repo_url, local_path) Repo.clone_from(repo_url, local_path)
repo = Repo.clone_from(repo_url, local_path)
logging.info("Clone completed.") logging.info("Clone completed.")
return repo.head.commit.tree
def _load_file(file_path: str): def _get_repo_tree_contents(repo_path, tree, progress_bar):
try: for subtree in tree:
data_type = detect_datatype(file_path).value if subtree.type == "tree":
except Exception: _get_repo_tree_contents(repo_path, subtree, progress_bar)
data_type = "unstructured" else:
assert subtree.type == "blob"
if data_type == "mdx":
data = mdx_loader.load_data(file_path)
elif data_type == "json":
data = json_loader.load_data(file_path)
else:
data = _load_file_data(file_path)
return data.get("data", [])
def _is_file_empty(file_path):
return os.path.getsize(file_path) == 0
def _is_whitelisted(file_path):
whitelisted_extensions = ["md", "txt", "html", "json", "py", "js", "jsx", "ts", "tsx", "mdx", "rst"]
_, file_extension = os.path.splitext(file_path)
return file_extension[1:] in whitelisted_extensions
def _add_repo_files(repo_path: str):
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
future_to_file = {
executor.submit(_load_file, os.path.join(root, filename)): os.path.join(root, filename)
for root, _, files in os.walk(repo_path)
for filename in files
if _is_whitelisted(os.path.join(root, filename))
and not _is_file_empty(os.path.join(root, filename)) # noqa:E501
}
for future in tqdm(concurrent.futures.as_completed(future_to_file), total=len(future_to_file)):
file = future_to_file[future]
try: try:
results = future.result() contents = subtree.data_stream.read().decode("utf-8")
if results: except Exception:
data.extend(results) logging.warning(f"Failed to read file: {subtree.path}")
data_urls.extend([result.get("meta_data").get("url") for result in results]) progress_bar.update(1) if progress_bar else None
except Exception as e: continue
logging.warn(f"Failed to process {file}: {e}")
url = f"{repo_url.rstrip('.git')}/blob/main/{subtree.path}"
data.append(
{
"content": clean_string(contents),
"meta_data": {
"url": url,
},
}
)
if progress_bar is not None:
progress_bar.update(1)
repo_tree = _get_repo_tree(repo_url, local_path)
tree_list = list(repo_tree.traverse())
with tqdm(total=len(tree_list), desc="Loading files:", unit="item") as progress_bar:
_get_repo_tree_contents(local_path, repo_tree, progress_bar)
return data
def _github_search_repo(self, query: str):
"""Search github repo."""
data = []
logging.info(f"Searching github repos with query: {query}")
results = self.client.search_repositories(query)
# Add repo urls and descriptions
urls = list(map(lambda x: x.html_url, results))
discriptions = list(map(lambda x: x.description, results))
data.append(
{
"content": clean_string(desc),
"meta_data": {
"url": url,
},
}
for url, desc in zip(urls, discriptions)
)
# Add repo contents
for result in results:
clone_url = result.clone_url
logging.info(f"Cloning repository: {clone_url}")
data = self._get_github_repo_data(clone_url)
return data
def _github_search_issues_and_pr(self, query: str, type: str):
"""Search github issues and PRs."""
data = []
query = f"{query} is:{type}"
logging.info(f"Searching github for query: {query}")
results = self.client.search_issues(query)
logging.info(f"Total results: {results.totalCount}")
for result in tqdm(results, total=results.totalCount, desc=f"Loading {type} from github"):
url = result.html_url
title = result.title
body = result.body
if not body:
logging.warn(f"Skipping issue because empty content for: {url}")
continue
labels = " ".join([label.name for label in result.labels])
issue_comments = result.get_comments()
comments = []
comments_created_at = []
for comment in issue_comments:
comments_created_at.append(str(comment.created_at))
comments.append(f"{comment.user.name}:{comment.body}")
content = "\n".join([title, labels, body, *comments])
metadata = {
"url": url,
"created_at": str(result.created_at),
"comments_created_at": " ".join(comments_created_at),
}
data.append(
{
"content": clean_string(content),
"meta_data": metadata,
}
)
return data
# need to test more for discussion
def _github_search_discussions(self, query: str):
"""Search github discussions."""
data = []
query = f"{query} is:discussion"
logging.info(f"Searching github repo for query: {query}")
repos_results = self.client.search_repositories(query)
logging.info(f"Total repos found: {repos_results.totalCount}")
for repo_result in tqdm(repos_results, total=repos_results.totalCount, desc="Loading discussions from github"):
teams = repo_result.get_teams()
for team in teams:
team_discussions = team.get_discussions()
for discussion in team_discussions:
url = discussion.html_url
title = discussion.title
body = discussion.body
if not body:
logging.warn(f"Skipping discussion because empty content for: {url}")
continue
comments = []
comments_created_at = []
print("Discussion comments: ", discussion.comments_url)
content = "\n".join([title, body, *comments])
metadata = {
"url": url,
"created_at": str(discussion.created_at),
"comments_created_at": " ".join(comments_created_at),
}
data.append(
{
"content": clean_string(content),
"meta_data": metadata,
}
)
return data
def _search_github_data(self, search_type: str, query: str):
"""Search github data."""
if search_type == "code":
data = self._github_search_code(query)
elif search_type == "repo":
data = self._github_search_repo(query)
elif search_type == "issue":
data = self._github_search_issues_and_pr(query, search_type)
elif search_type == "pr":
data = self._github_search_issues_and_pr(query, search_type)
elif search_type == "discussion":
raise ValueError("GithubLoader does not support searching discussions yet.")
return data
def _get_valid_github_query(self, query: str):
"""Check if query is valid and return search types and valid github query."""
query_terms = shlex.split(query)
# query must provide repo to load data from
if len(query_terms) < 1 or "repo:" not in query:
raise ValueError(
"GithubLoader requires a search query with `repo:` term. Refer docs - `https://docs.embedchain.ai/data-sources/github`" # noqa: E501
)
github_query = []
types = set()
type_pattern = r"type:([a-zA-Z,]+)"
for term in query_terms:
term_match = re.search(type_pattern, term)
if term_match:
search_types = term_match.group(1).split(",")
types.update(search_types)
else:
github_query.append(term)
# query must provide search type
if len(types) == 0:
raise ValueError(
"GithubLoader requires a search query with `type:` term. Refer docs - `https://docs.embedchain.ai/data-sources/github`" # noqa: E501
)
for search_type in search_types:
if search_type not in VALID_SEARCH_TYPES:
raise ValueError(
f"Invalid search type: {search_type}. Valid types are: {', '.join(VALID_SEARCH_TYPES)}"
)
query = " ".join(github_query)
return types, query
def load_data(self, search_query: str, max_results: int = 1000):
"""Load data from github search query."""
if not self.client:
raise ValueError(
"GithubLoader client is not initialized, data will not be loaded. Refer docs - `https://docs.embedchain.ai/data-sources/github`" # noqa: E501
)
search_types, query = self._get_valid_github_query(search_query)
logging.info(f"Searching github for query: {query}, with types: {', '.join(search_types)}")
data = []
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
futures_map = executor.map(self._search_github_data, search_types, [query] * len(search_types))
for search_data in tqdm(futures_map, total=len(search_types), desc="Searching data from github"):
data.extend(search_data)
source_hash = hashlib.sha256(repo_url.encode()).hexdigest()
repo_path = f"/tmp/{source_hash}"
_fetch_or_clone_repo(repo_url=repo_url, local_path=repo_path)
_add_repo_files(repo_path)
doc_id = hashlib.sha256((repo_url + ", ".join(data_urls)).encode()).hexdigest()
return { return {
"doc_id": doc_id, "doc_id": hashlib.sha256(query.encode()).hexdigest(),
"data": data, "data": data,
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
+1 -1
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
+1 -1
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
+1 -1
View File
@@ -10,7 +10,7 @@ except ImportError:
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+1 -1
View File
@@ -6,7 +6,7 @@ except ImportError:
raise ImportError( raise ImportError(
'PDF File requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'PDF File requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+15 -12
View File
@@ -1,6 +1,7 @@
import concurrent.futures import concurrent.futures
import hashlib import hashlib
import logging import logging
from urllib.parse import urlparse
import requests import requests
from tqdm import tqdm from tqdm import tqdm
@@ -13,10 +14,9 @@ except ImportError:
'Sitemap requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'Sitemap requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.loaders.web_page import WebPageLoader from embedchain.loaders.web_page import WebPageLoader
from embedchain.utils import is_readable
@register_deserializable @register_deserializable
@@ -30,29 +30,32 @@ class SitemapLoader(BaseLoader):
def load_data(self, sitemap_url): def load_data(self, sitemap_url):
output = [] output = []
web_page_loader = WebPageLoader() web_page_loader = WebPageLoader()
response = requests.get(sitemap_url)
response.raise_for_status()
soup = BeautifulSoup(response.text, "xml") if urlparse(sitemap_url).scheme not in ["file", "http", "https"]:
raise ValueError("Not a valid URL.")
if urlparse(sitemap_url).scheme in ["http", "https"]:
response = requests.get(sitemap_url)
response.raise_for_status()
else:
with open(sitemap_url, "r") as file:
soup = BeautifulSoup(file, "xml")
links = [link.text for link in soup.find_all("loc") if link.parent.name == "url"] links = [link.text for link in soup.find_all("loc") if link.parent.name == "url"]
if len(links) == 0: if len(links) == 0:
links = [link.text for link in soup.find_all("loc")] links = [link.text for link in soup.find_all("loc")]
doc_id = hashlib.sha256((" ".join(links) + sitemap_url).encode()).hexdigest() doc_id = hashlib.sha256((" ".join(links) + sitemap_url).encode()).hexdigest()
def load_link(link): def load_web_page(link):
try: try:
each_load_data = web_page_loader.load_data(link) loader_data = web_page_loader.load_data(link)
if is_readable(each_load_data.get("data")[0].get("content")): return loader_data.get("data")
return each_load_data.get("data")
else:
logging.warning(f"Page is not readable (too many invalid characters): {link}")
except ParserRejectedMarkup as e: except ParserRejectedMarkup as e:
logging.error(f"Failed to parse {link}: {e}") logging.error(f"Failed to parse {link}: {e}")
return None return None
with concurrent.futures.ThreadPoolExecutor() as executor: with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_link = {executor.submit(load_link, link): link for link in links} future_to_link = {executor.submit(load_web_page, link): link for link in links}
for future in tqdm(concurrent.futures.as_completed(future_to_link), total=len(links), desc="Loading pages"): for future in tqdm(concurrent.futures.as_completed(future_to_link), total=len(links), desc="Loading pages"):
link = future_to_link[future] link = future_to_link[future]
try: try:
+1 -1
View File
@@ -4,7 +4,7 @@ import time
import requests import requests
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import is_readable from embedchain.utils import is_readable
+1 -1
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+1 -1
View File
@@ -10,7 +10,7 @@ except ImportError:
'Webpage requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'Webpage requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+1 -1
View File
@@ -6,7 +6,7 @@ except ImportError:
raise ImportError( raise ImportError(
'XML file requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'XML file requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+1 -1
View File
@@ -6,7 +6,7 @@ except ImportError:
raise ImportError( raise ImportError(
'YouTube video requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`' 'YouTube video requires extra dependencies. Install with `pip install --upgrade "embedchain[dataloaders]"`'
) from None ) from None
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.loaders.base_loader import BaseLoader from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string from embedchain.utils import clean_string
+1 -1
View File
@@ -23,7 +23,7 @@ CHAT_MESSAGE_CREATE_TABLE_QUERY = """
class ECChatMemory: class ECChatMemory:
def __init__(self) -> None: def __init__(self) -> None:
with sqlite3.connect(SQLITE_PATH) as self.connection: with sqlite3.connect(SQLITE_PATH, check_same_thread=False) as self.connection:
self.cursor = self.connection.cursor() self.cursor = self.connection.cursor()
self.cursor.execute(CHAT_MESSAGE_CREATE_TABLE_QUERY) self.cursor.execute(CHAT_MESSAGE_CREATE_TABLE_QUERY)
+1 -1
View File
@@ -1,7 +1,7 @@
import logging import logging
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from embedchain.helper.json_serializable import JSONSerializable from embedchain.helpers.json_serializable import JSONSerializable
class BaseMessage(JSONSerializable): class BaseMessage(JSONSerializable):
+4 -10
View File
@@ -29,13 +29,10 @@ class IndirectDataType(Enum):
JSON = "json" JSON = "json"
OPENAPI = "openapi" OPENAPI = "openapi"
GMAIL = "gmail" GMAIL = "gmail"
POSTGRES = "postgres"
MYSQL = "mysql"
SLACK = "slack"
DISCOURSE = "discourse"
SUBSTACK = "substack" SUBSTACK = "substack"
GITHUB = "github"
YOUTUBE_CHANNEL = "youtube_channel" YOUTUBE_CHANNEL = "youtube_channel"
DISCORD = "discord"
CUSTOM = "custom"
class SpecialDataType(Enum): class SpecialDataType(Enum):
@@ -64,10 +61,7 @@ class DataType(Enum):
JSON = IndirectDataType.JSON.value JSON = IndirectDataType.JSON.value
OPENAPI = IndirectDataType.OPENAPI.value OPENAPI = IndirectDataType.OPENAPI.value
GMAIL = IndirectDataType.GMAIL.value GMAIL = IndirectDataType.GMAIL.value
POSTGRES = IndirectDataType.POSTGRES.value
MYSQL = IndirectDataType.MYSQL.value
SLACK = IndirectDataType.SLACK.value
DISCOURSE = IndirectDataType.DISCOURSE.value
SUBSTACK = IndirectDataType.SUBSTACK.value SUBSTACK = IndirectDataType.SUBSTACK.value
GITHUB = IndirectDataType.GITHUB.value
YOUTUBE_CHANNEL = IndirectDataType.YOUTUBE_CHANNEL.value YOUTUBE_CHANNEL = IndirectDataType.YOUTUBE_CHANNEL.value
DISCORD = IndirectDataType.DISCORD.value
CUSTOM = IndirectDataType.CUSTOM.value
+56 -30
View File
@@ -4,6 +4,7 @@ import logging
import os import os
import sqlite3 import sqlite3
import uuid import uuid
from typing import Any, Dict, Optional
import requests import requests
import yaml import yaml
@@ -15,14 +16,17 @@ from embedchain.embedchain import EmbedChain
from embedchain.embedder.base import BaseEmbedder from embedchain.embedder.base import BaseEmbedder
from embedchain.embedder.openai import OpenAIEmbedder from embedchain.embedder.openai import OpenAIEmbedder
from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory
from embedchain.helper.json_serializable import register_deserializable from embedchain.helpers.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm from embedchain.llm.base import BaseLlm
from embedchain.llm.openai import OpenAILlm from embedchain.llm.openai import OpenAILlm
from embedchain.telemetry.posthog import AnonymousTelemetry from embedchain.telemetry.posthog import AnonymousTelemetry
from embedchain.utils import validate_yaml_config from embedchain.utils import validate_config
from embedchain.vectordb.base import BaseVectorDB from embedchain.vectordb.base import BaseVectorDB
from embedchain.vectordb.chroma import ChromaDB from embedchain.vectordb.chroma import ChromaDB
# Setup the user directory if doesn't exist already
Client.setup_dir()
@register_deserializable @register_deserializable
class Pipeline(EmbedChain): class Pipeline(EmbedChain):
@@ -40,7 +44,7 @@ class Pipeline(EmbedChain):
db: BaseVectorDB = None, db: BaseVectorDB = None,
embedding_model: BaseEmbedder = None, embedding_model: BaseEmbedder = None,
llm: BaseLlm = None, llm: BaseLlm = None,
yaml_path: str = None, config_data: dict = None,
log_level=logging.WARN, log_level=logging.WARN,
auto_deploy: bool = False, auto_deploy: bool = False,
chunker: ChunkerConfig = None, chunker: ChunkerConfig = None,
@@ -56,18 +60,15 @@ class Pipeline(EmbedChain):
:type embedding_model: BaseEmbedder, optional :type embedding_model: BaseEmbedder, optional
:param llm: The LLM model used to calculate embeddings, defaults to None :param llm: The LLM model used to calculate embeddings, defaults to None
:type llm: BaseLlm, optional :type llm: BaseLlm, optional
:param yaml_path: Path to the YAML configuration file, defaults to None :param config_data: Config dictionary, defaults to None
:type yaml_path: str, optional :type config_data: dict, optional
:param log_level: Log level to use, defaults to logging.WARN :param log_level: Log level to use, defaults to logging.WARN
:type log_level: int, optional :type log_level: int, optional
:param auto_deploy: Whether to deploy the pipeline automatically, defaults to False :param auto_deploy: Whether to deploy the pipeline automatically, defaults to False
:type auto_deploy: bool, optional :type auto_deploy: bool, optional
:raises Exception: If an error occurs while creating the pipeline :raises Exception: If an error occurs while creating the pipeline
""" """
# Setup user directory if it doesn't exist already if id and config_data:
Client.setup_dir()
if id and yaml_path:
raise Exception("Cannot provide both id and config. Please provide only one of them.") raise Exception("Cannot provide both id and config. Please provide only one of them.")
if id and name: if id and name:
@@ -79,8 +80,8 @@ class Pipeline(EmbedChain):
logging.basicConfig(level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") logging.basicConfig(level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.auto_deploy = auto_deploy self.auto_deploy = auto_deploy
# Store the yaml config as an attribute to be able to send it # Store the dict config as an attribute to be able to send it
self.yaml_config = None self.config_data = config_data if (config_data and validate_config(config_data)) else None
self.client = None self.client = None
# pipeline_id from the backend # pipeline_id from the backend
self.id = None self.id = None
@@ -92,11 +93,6 @@ class Pipeline(EmbedChain):
self.name = self.config.name self.name = self.config.name
self.config.id = self.local_id = str(uuid.uuid4()) if self.config.id is None else self.config.id self.config.id = self.local_id = str(uuid.uuid4()) if self.config.id is None else self.config.id
if yaml_path:
with open(yaml_path, "r") as file:
config_data = yaml.safe_load(file)
self.yaml_config = config_data
if id is not None: if id is not None:
# Init client first since user is trying to fetch the pipeline # Init client first since user is trying to fetch the pipeline
# details from the platform # details from the platform
@@ -187,9 +183,9 @@ class Pipeline(EmbedChain):
Create a pipeline on the platform. Create a pipeline on the platform.
""" """
print("🛠️ Creating pipeline on the platform...") print("🛠️ Creating pipeline on the platform...")
# self.yaml_config is a dict. Pass it inside the key 'yaml_config' to the backend # self.config_data is a dict. Pass it inside the key 'yaml_config' to the backend
payload = { payload = {
"yaml_config": json.dumps(self.yaml_config), "yaml_config": json.dumps(self.config_data),
"name": self.name, "name": self.name,
"local_id": self.local_id, "local_id": self.local_id,
} }
@@ -346,27 +342,57 @@ class Pipeline(EmbedChain):
self.telemetry.capture(event_name="deploy", properties=self._telemetry_props) self.telemetry.capture(event_name="deploy", properties=self._telemetry_props)
@classmethod @classmethod
def from_config(cls, yaml_path: str, auto_deploy: bool = False): def from_config(
cls,
config_path: Optional[str] = None,
config: Optional[Dict[str, Any]] = None,
auto_deploy: bool = False,
yaml_path: Optional[str] = None,
):
""" """
Instantiate a Pipeline object from a YAML configuration file. Instantiate a Pipeline object from a configuration.
:param yaml_path: Path to the YAML configuration file. :param config_path: Path to the YAML or JSON configuration file.
:type yaml_path: str :type config_path: Optional[str]
:param config: A dictionary containing the configuration.
:type config: Optional[Dict[str, Any]]
:param auto_deploy: Whether to deploy the pipeline automatically, defaults to False :param auto_deploy: Whether to deploy the pipeline automatically, defaults to False
:type auto_deploy: bool, optional :type auto_deploy: bool, optional
:param yaml_path: (Deprecated) Path to the YAML configuration file. Use config_path instead.
:type yaml_path: Optional[str]
:return: An instance of the Pipeline class. :return: An instance of the Pipeline class.
:rtype: Pipeline :rtype: Pipeline
""" """
# Setup user directory if it doesn't exist already # Backward compatibility for yaml_path
Client.setup_dir() if yaml_path and not config_path:
config_path = yaml_path
with open(yaml_path, "r") as file: if config_path and config:
config_data = yaml.safe_load(file) raise ValueError("Please provide only one of config_path or config.")
config_data = None
if config_path:
file_extension = os.path.splitext(config_path)[1]
with open(config_path, "r") as file:
if file_extension in [".yaml", ".yml"]:
config_data = yaml.safe_load(file)
elif file_extension == ".json":
config_data = json.load(file)
else:
raise ValueError("config_path must be a path to a YAML or JSON file.")
elif config and isinstance(config, dict):
config_data = config
else:
logging.error(
"Please provide either a config file path (YAML or JSON) or a config dictionary. Falling back to defaults because no config is provided.", # noqa: E501
)
config_data = {}
try: try:
validate_yaml_config(config_data) validate_config(config_data)
except Exception as e: except Exception as e:
raise Exception(f"❌ Error occurred while validating the YAML config. Error: {str(e)}") raise Exception(f"Error occurred while validating the config. Error: {str(e)}")
pipeline_config_data = config_data.get("app", {}).get("config", {}) pipeline_config_data = config_data.get("app", {}).get("config", {})
db_config_data = config_data.get("vectordb", {}) db_config_data = config_data.get("vectordb", {})
@@ -391,7 +417,7 @@ class Pipeline(EmbedChain):
) )
# Send anonymous telemetry # Send anonymous telemetry
event_properties = {"init_type": "yaml_config"} event_properties = {"init_type": "config_data"}
AnonymousTelemetry().capture(event_name="init", properties=event_properties) AnonymousTelemetry().capture(event_name="init", properties=event_properties)
return cls( return cls(
@@ -399,7 +425,7 @@ class Pipeline(EmbedChain):
llm=llm, llm=llm,
db=db, db=db,
embedding_model=embedding_model, embedding_model=embedding_model,
yaml_path=yaml_path, config_data=config_data,
auto_deploy=auto_deploy, auto_deploy=auto_deploy,
chunker=chunker_config_data, chunker=chunker_config_data,
) )
+5 -2
View File
@@ -10,7 +10,7 @@ from typing import cast
from openai import OpenAI from openai import OpenAI
from openai.types.beta.threads import MessageContentText, ThreadMessage from openai.types.beta.threads import MessageContentText, ThreadMessage
from embedchain import Pipeline from embedchain import Client, Pipeline
from embedchain.config import AddConfig from embedchain.config import AddConfig
from embedchain.data_formatter import DataFormatter from embedchain.data_formatter import DataFormatter
from embedchain.models.data_type import DataType from embedchain.models.data_type import DataType
@@ -19,6 +19,9 @@ from embedchain.utils import detect_datatype
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
# Setup the user directory if doesn't exist already
Client.setup_dir()
class OpenAIAssistant: class OpenAIAssistant:
def __init__( def __init__(
@@ -162,7 +165,7 @@ class AIAssistant:
self.instructions = instructions self.instructions = instructions
self.assistant_id = assistant_id or str(uuid.uuid4()) self.assistant_id = assistant_id or str(uuid.uuid4())
self.thread_id = thread_id or str(uuid.uuid4()) self.thread_id = thread_id or str(uuid.uuid4())
self.pipeline = Pipeline.from_config(yaml_path=yaml_path) if yaml_path else Pipeline() self.pipeline = Pipeline.from_config(config_path=yaml_path) if yaml_path else Pipeline()
self.pipeline.local_id = self.pipeline.config.id = self.thread_id self.pipeline.local_id = self.pipeline.config.id = self.thread_id
if self.instructions: if self.instructions:

Some files were not shown because too many files have changed in this diff Show More