Compare commits

...

48 Commits

Author SHA1 Message Date
Deven Patel deaa7f50f8 [Bug Fix] fix chromadb where clause for query and delete (#937)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-10 16:04:25 -08:00
Sidharth Mohanty 744ab5156f [Bug fix] missing dir on first init for App (#934) 2023-11-10 10:15:04 -08:00
Sidharth Mohanty c45413969a [refactor] Use pipeline for bots instead of App (#936) 2023-11-10 10:13:21 -08:00
Deshraj Yadav b314e5e080 [bug] Fix issue of missing user directory on first init (#931) 2023-11-09 22:29:41 -08:00
Deshraj Yadav 17129e2eaa [Improvement] Add support for reloading history for an existing app (#930) 2023-11-09 15:17:51 -08:00
Deven Patel 654fd8d74c [Improvement] Use SQLite for chat memory (#910)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-09 13:56:28 -08:00
Sidharth Mohanty 9d3568ef75 Update package version to 0.1.3 (#928) 2023-11-09 11:33:18 -08:00
Sidharth Mohanty 14712cac88 Deploy remaining bots and fix schema validation (#927) 2023-11-09 10:44:47 -08:00
Deshraj Yadav 0d568c758b [Feat] Add anonymous telemetry to assistant (#924) 2023-11-09 02:07:47 -08:00
Deshraj Yadav 7c6b88c7c5 [Docs] Update docs and improve assistant api (#923) 2023-11-09 01:16:19 -08:00
Deshraj Yadav 32c93be46e [chore] update poetry.lock file (#922) 2023-11-09 00:59:29 -08:00
Deven Patel 7de8d85199 [Feature] Add Postgres data loader (#918)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-08 23:50:46 -08:00
Deshraj Yadav f7dd65a3de [Feature] Add support for OpenAI assistants and support openai version >=1.0.0 (#921) 2023-11-08 22:49:03 -08:00
Sidharth Mohanty d8cdbe0041 Set check_same_thread false so that one App can be used in parallel (#911) 2023-11-08 10:26:31 -08:00
Sidharth Mohanty 2b8b6d3ea9 Chunker config docs (#913) 2023-11-08 10:25:45 -08:00
Sidharth Mohanty 936c7e389f Deploy Full stack docker image (#914) 2023-11-08 10:25:12 -08:00
Sidharth Mohanty 6864b4207b Dockerize discord bot and update docs to run the bot correctly (#919) 2023-11-08 10:24:57 -08:00
Deshraj Yadav 98eb5b54be [Docs] Update developer documentation (#916) 2023-11-07 19:07:47 -08:00
Deshraj Yadav 3332e6e236 [Docs] Update README (#915) 2023-11-07 18:12:27 -08:00
Deven Patel 0533da72d7 [Improvement] add delete functionality to zilliz DB (#912)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-07 02:46:39 -08:00
Sidharth Mohanty a1de238716 Introduce chunker config in yaml config (#907) 2023-11-06 09:43:15 -08:00
Deshraj Yadav f0d112254b [version] bump package version and minor cleanup (#909) 2023-11-05 16:34:36 -08:00
Sidharth Mohanty 830a7397ef Add yaml config validation (#890) 2023-11-04 22:23:55 -07:00
Deven Patel 5428765329 Beautify JSON docs (#906)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-03 13:52:33 -07:00
Sidharth Mohanty 23c912f2b7 Improve getting started page by adding steps (#904) 2023-11-03 13:40:40 -07:00
Sidharth Mohanty 9c4b023297 Use either embedder or embedding_model as YAML key (#905) 2023-11-03 13:40:16 -07:00
Deven Patel 53037b5ed8 [Feature Improvement] Update JSON Loader to support loading data from more sources (#898)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-03 10:00:27 -07:00
Sidharth Mohanty e2546a653d [chore] fix rest api docs and other minor fixes (#902) 2023-11-03 09:40:48 -07:00
Deshraj Yadav 4b8cada873 [REST API] Change docker image name and update docs (#901) 2023-11-03 01:08:27 -07:00
Deshraj Yadav fa3ca1d08a [REST API] Incorporate changes related to REST API docs (#900) 2023-11-03 00:42:55 -07:00
Sidharth Mohanty 8dd5cb9602 Add rest-api example (#889) 2023-11-03 00:32:51 -07:00
Deven Patel a054f7be9c [Data loader] Make json data loader work for other languages (#897)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-01 22:27:40 -07:00
Deven Patel df314dc6d1 Clean json data before loading (#895)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-01 21:52:34 -07:00
Deven Patel 930280f4ce [Feature] Add citations flag in query and chat functions of App to return context along with the answer (#859) 2023-11-01 13:06:28 -07:00
Deshraj Yadav 5022c1ae29 [misc] update poetry.lock file (#891) 2023-11-01 11:35:44 -07:00
Deshraj Yadav 6ced756a6b [misc] add json extra in pyproject.toml file (#886) 2023-10-31 23:55:25 -07:00
Deshraj Yadav b17268db50 [misc] remove jq as a dependency (#885) 2023-10-31 21:02:19 -07:00
Deshraj Yadav 476da37009 [version] bump package version to v0.0.87 (#883) 2023-10-31 12:40:02 -07:00
Deshraj Yadav 455f059c6f [Telemetry] Update anonymous telemetry API key (#882) 2023-10-31 12:34:54 -07:00
Deven Patel 5255a37c93 Embedchain json url support (#878)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-10-30 16:19:11 -07:00
Deven Patel 68dc274f72 Embedchain json loader update (#876)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-10-30 15:30:49 -07:00
Deshraj Yadav 30228f7f8e [version] Update langchain to v0.0.303 (#875) 2023-10-30 15:02:38 -07:00
Sidharth Mohanty e15ef79ca9 Lazy load loaders and chunkers (#872) 2023-10-30 11:20:38 -07:00
Deshraj Yadav bc012a7518 [Docs] Update embedchain docs and analytics (#871) 2023-10-30 00:38:35 -07:00
Sidharth Mohanty 3b4409cfad Update notebooks to work with the latest version (#870) 2023-10-29 23:06:43 -07:00
Deshraj Yadav d3726134b2 [Docs] Update docs and minor improvements in search API (#869) 2023-10-29 16:50:14 -07:00
anujshandillya 5acb7f1c55 [fix]: updated twitter logo to new X (#868) 2023-10-29 14:37:32 -07:00
Deshraj Yadav 81336668b3 [Feature]: Add posthog anonymous telemetry and update docs (#867) 2023-10-29 01:20:21 -07:00
170 changed files with 5031 additions and 1419 deletions
-1
View File
@@ -76,7 +76,6 @@ docs/_build/
target/
# Jupyter Notebook
*.yaml
# IPython
profile_default/
+74 -73
View File
@@ -1,70 +1,68 @@
# embedchain
<p align="center">
<img src="docs/logo/dark.svg" width="400px" alt="Embedchain Logo">
</p>
<a href="https://runacap.com/ross-index/q3-2023/" target="_blank" rel="noopener"><img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2023/10/ROSS_badge_black_Q3_2023.svg" alt="ROSS Index - Fastest Growing Open-Source Startups in Q3 2023 | Runa Capital" width="260" height="56"/></a>
<p align="center">
<a href="https://runacap.com/ross-index/q3-2023/" target="_blank" rel="noopener"><img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2023/10/ROSS_badge_black_Q3_2023.svg" alt="ROSS Index - Fastest Growing Open-Source Startups in Q3 2023 | Runa Capital" width="260" height="56"/></a>
</p>
[![PyPI](https://img.shields.io/pypi/v/embedchain)](https://pypi.org/project/embedchain/)
[![Slack](https://img.shields.io/badge/slack-embedchain-brightgreen.svg?logo=slack)](https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw)
[![Discord](https://dcbadge.vercel.app/api/server/6PzXDgEjG5?style=flat)](https://discord.gg/CUU9FPhRNt)
[![Twitter](https://img.shields.io/twitter/follow/embedchain)](https://twitter.com/embedchain)
[![Substack](https://img.shields.io/badge/Substack-%23006f5c.svg?logo=substack)](https://embedchain.substack.com/)
[![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/138lMWhENGeEu7Q1-6lNbNTHGLZXBBz_B?usp=sharing)
[![codecov](https://codecov.io/gh/embedchain/embedchain/graph/badge.svg?token=EMRRHZXW1Q)](https://codecov.io/gh/embedchain/embedchain)
<p align="center">
<a href="https://pypi.org/project/embedchain/">
<img src="https://img.shields.io/pypi/v/embedchain" alt="PyPI">
</a>
<a href="https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw">
<img src="https://img.shields.io/badge/slack-embedchain-brightgreen.svg?logo=slack" alt="Slack">
</a>
<a href="https://discord.gg/CUU9FPhRNt">
<img src="https://dcbadge.vercel.app/api/server/6PzXDgEjG5?style=flat" alt="Discord">
</a>
<a href="https://twitter.com/embedchain">
<img src="https://img.shields.io/twitter/follow/embedchain" alt="Twitter">
</a>
<a href="https://embedchain.substack.com/">
<img src="https://img.shields.io/badge/Substack-%23006f5c.svg?logo=substack" alt="Substack">
</a>
<a href="https://colab.research.google.com/drive/138lMWhENGeEu7Q1-6lNbNTHGLZXBBz_B?usp=sharing">
<img src="https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667" alt="Open in Colab">
</a>
<a href="https://codecov.io/gh/embedchain/embedchain">
<img src="https://codecov.io/gh/embedchain/embedchain/graph/badge.svg?token=EMRRHZXW1Q" alt="codecov">
</a>
</p>
Embedchain is a Data Platform for LLMs - load, index, retrieve, and sync any unstructured data. Using embedchain, you can easily create LLM powered apps over any data. If you want a javascript version, check out [embedchain-js](https://github.com/embedchain/embedchain/tree/main/embedchain-js)
<hr />
## Community
* Join embedchain community on slack by accepting [this invite](https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw)
## 🤝 Schedule a 1-on-1 Session
Book a [1-on-1 Session](https://cal.com/taranjeetio/ec) with Taranjeet, the founder, to discuss any issues, provide feedback, or explore how we can improve Embedchain for you.
## What is Embedchain?
Embedchain is a Data Platform for Large Language Models (LLMs). Seamlessly load, index, retrieve, and sync unstructured data to build dynamic, LLM-powered applications. Check out [embedchain-js](https://github.com/embedchain/embedchain/tree/main/embedchain-js) for a JavaScript implementation.
## 🔧 Quick install
### Python API
```bash
pip install --upgrade embedchain
```
## 🔍 Demo
### REST API
You can also run Embedchain as a REST API server using the following command:
Try out embedchain in your browser:
```bash
docker run --name embedchain -p 8080:8080 embedchain/rest-api:latest
```
[![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing)
Then, navigate to http://0.0.0.0:8080/docs to interact with the API.
## 📖 Documentation
## 🔍 Usage and Demo
The documentation for embedchain can be found at [docs.embedchain.ai](https://docs.embedchain.ai).
<!-- Demo GIF or Image -->
<p align="center">
<img src="docs/images/cover.gif" width="900px" alt="Embedchain Demo">
</p>
## 💻 Usage
Embedchain empowers you to create ChatGPT like apps, on your own dynamic dataset.
### Data types supported
* Youtube video
* PDF file
* CSV file
* Web page
* MDX file
* XML file
* Sitemap
* Doc file
* Notion
* JSON file
* OpenAPI specs
* Code docs website
* Unstructured file loader and many more
You can find the full list of data types on [our documentation](https://docs.embedchain.ai/data-sources/).
### Queries
For example, you can use Embedchain to create an Elon Musk bot using the following code:
For example, you can create an Elon Musk bot using the following code:
```python
import os
from embedchain import App
from embedchain import Pipeline as App
# Create a bot instance
os.environ["OPENAI_API_KEY"] = "YOUR API KEY"
@@ -78,37 +76,40 @@ elon_bot.add("https://www.youtube.com/watch?v=RcYjXbSJBN8")
# Query the bot
elon_bot.query("How many companies does Elon Musk run and name those?")
# Answer: Elon Musk currently runs several companies. As of my knowledge, he is the CEO and lead designer of SpaceX, the CEO and product architect of Tesla, Inc., the CEO and founder of Neuralink, and the CEO and founder of The Boring Company. However, please note that this information may change over time, so it's always good to verify the latest updates.
# (Optional): Deploy app to Embedchain Platform
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.
```
## Examples
You can also try it in your browser with Google Colab:
| LLM | Google Colab | Replit |
|--------------|---------------|----------|
| OpenAI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/openai.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/openai#main.py) |
| Anthropic | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/anthropic.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/anthropic#main.py) |
| Azure OpenAI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/azure-openai.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/azureopenai#main.py) |
| VertexAI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/vertex_ai.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/vertexai#main.py) |
| Cohere | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/cohere.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/cohere#main.py) |
| Hugging Face | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/hugging_face_hub.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/huggingface#main.py) |
| JinaChat | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/jina.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/jina#main.py) |
| GPT4All | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/gpt4all.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/gpt4all#main.py) |
| Llama2 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/llama2.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/llama2#main.py) |
[![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing)
| Embedding model | Google Colab | Replit |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| OpenAI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/openai.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/openai#main.py) |
| VertexAI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/vertex_ai.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/vertexai#main.py) |
| GPT4All | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/gpt4all.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/gpt4all#main.py) |
| Hugging Face | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/hugging_face_hub.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/huggingface#main.py) |
## 📖 Documentation
Comprehensive guides and API documentation are available to help you get the most out of Embedchain:
| Vector DB | Google Colab | Replit |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| ChromaDB | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/chromadb.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/chromadb#main.py) |
| Elasticsearch | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/elasticsearch.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/elasticsearchdb#main.py) |
| Opensearch | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/opensearch.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/opensearchdb#main.py) |
| Pinecone | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/embedchain/embedchain/blob/main/notebooks/pinecone.ipynb) | [![Try with Replit Badge](https://replit.com/badge?caption=Try%20with%20Replit&variant=small)](https://replit.com/@taranjeetio/pineconedb#main.py) |
- [Getting Started](https://docs.embedchain.ai/get-started/quickstart)
- [Introduction](https://docs.embedchain.ai/get-started/introduction#what-is-embedchain)
- [Examples](https://docs.embedchain.ai/get-started/examples)
- [Supported data types](https://docs.embedchain.ai/data-sources/)
## 🤝 Contributing
## 🔗 Join the Community
Connect with fellow developers and users by joining our [Slack Workspace](https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw). Dive into discussions, ask questions, and share your experiences.
## 🤝 Schedule a 1-on-1 Session
Book a [1-on-1 Session](https://cal.com/taranjeetio/ec) with Taranjeet, the founder, to discuss any issues, provide feedback, or explore how we can improve Embedchain for you.
## 🌐 Contributing
Contributions are welcome! Please check out the issues on the repository, and feel free to open a pull request.
For more information, please see the [contributing guidelines](CONTRIBUTING.md).
+1 -1
View File
@@ -1,7 +1,7 @@
llm:
provider: anthropic
model: 'claude-instant-1'
config:
model: 'claude-instant-1'
temperature: 0.5
max_tokens: 1000
top_p: 1
+1 -1
View File
@@ -4,8 +4,8 @@ app:
llm:
provider: azure_openai
model: gpt-35-turbo
config:
model: gpt-35-turbo
deployment_name: your_llm_deployment_name
temperature: 0.5
max_tokens: 1000
+2 -2
View File
@@ -5,8 +5,8 @@ app:
llm:
provider: openai
model: 'gpt-3.5-turbo'
config:
model: 'gpt-3.5-turbo'
temperature: 0.5
max_tokens: 1000
top_p: 1
@@ -23,4 +23,4 @@ embedder:
provider: openai
config:
model: 'text-embedding-ada-002'
deployment_name: null
deployment_name: 'test-deployment'
+4
View File
@@ -0,0 +1,4 @@
chunker:
chunk_size: 100
chunk_overlap: 20
length_function: 'len'
+1 -1
View File
@@ -1,7 +1,7 @@
llm:
provider: cohere
model: large
config:
model: large
temperature: 0.5
max_tokens: 1000
top_p: 1
+6 -1
View File
@@ -2,10 +2,15 @@ app:
config:
id: 'full-stack-app'
chunker:
chunk_size: 100
chunk_overlap: 20
length_function: 'len'
llm:
provider: openai
model: 'gpt-3.5-turbo'
config:
model: 'gpt-3.5-turbo'
temperature: 0.5
max_tokens: 1000
top_p: 1
+1 -1
View File
@@ -1,7 +1,7 @@
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
+1 -1
View File
@@ -1,7 +1,7 @@
llm:
provider: huggingface
model: 'google/flan-t5-xxl'
config:
model: 'google/flan-t5-xxl'
temperature: 0.5
max_tokens: 1000
top_p: 0.5
+1 -1
View File
@@ -1,7 +1,7 @@
llm:
provider: llama2
model: 'a16z-infra/llama13b-v2-chat:df7690f1994d94e96ad9d568eac121aecf50684a0b0963b25a41cc40061269e5'
config:
model: 'a16z-infra/llama13b-v2-chat:df7690f1994d94e96ad9d568eac121aecf50684a0b0963b25a41cc40061269e5'
temperature: 0.5
max_tokens: 1000
top_p: 0.5
+3 -3
View File
@@ -1,14 +1,14 @@
app:
config:
id: 'my-app'
log_level: 'WARN'
log_level: 'WARNING'
collect_metrics: true
collection_name: 'my-app'
llm:
provider: openai
model: 'gpt-3.5-turbo'
config:
model: 'gpt-3.5-turbo'
temperature: 0.5
max_tokens: 1000
top_p: 1
@@ -30,4 +30,4 @@ embedder:
provider: openai
config:
model: 'text-embedding-ada-002'
deployment_name: null
deployment_name: 'my-app'
+2 -2
View File
@@ -7,7 +7,7 @@ app:
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
@@ -23,4 +23,4 @@ vectordb:
embedder:
provider: gpt4all
config:
deployment_name: null
deployment_name: 'test-deployment'
+1 -1
View File
@@ -13,7 +13,7 @@ vectordb:
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
+1 -1
View File
@@ -1,6 +1,6 @@
llm:
provider: vertexai
model: 'chat-bison'
config:
model: 'chat-bison'
temperature: 0.5
top_p: 0.5
+1 -3
View File
@@ -1,5 +1,4 @@
<Tip>
If you can't find the specific data source, please feel free to request through one of the following channels and help us prioritize.
<p>If you can't find the specific data source, please feel free to request through one of the following channels and help us prioritize.</p>
<CardGroup cols={2}>
<Card title="Slack" icon="slack" href="https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" color="#4A154B">
@@ -15,4 +14,3 @@ If you can't find the specific data source, please feel free to request through
Schedule a call with Embedchain founder
</Card>
</CardGroup>
</Tip>
+1 -3
View File
@@ -1,5 +1,4 @@
<Tip>
If you can't find the specific LLM you need, no need to fret. We're continuously expanding our support for additional LLMs, and you can help us prioritize by opening an issue on our GitHub or simply reaching out to us on our Slack or Discord community.
<p>If you can't find the specific LLM you need, no need to fret. We're continuously expanding our support for additional LLMs, and you can help us prioritize by opening an issue on our GitHub or simply reaching out to us on our Slack or Discord community.</p>
<CardGroup cols={2}>
<Card title="Slack" icon="slack" href="https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" color="#4A154B">
@@ -15,4 +14,3 @@ If you can't find the specific LLM you need, no need to fret. We're continuously
Schedule a call with Embedchain founder
</Card>
</CardGroup>
</Tip>
+3 -3
View File
@@ -1,5 +1,6 @@
<Tip>
If you can't find the specific vector database, please feel free to request through one of the following channels and help us prioritize.
<p>If you can't find the specific vector database, please feel free to request through one of the following channels and help us prioritize.</p>
<CardGroup cols={2}>
<Card title="Slack" icon="slack" href="https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" color="#4A154B">
@@ -15,4 +16,3 @@ If you can't find the specific vector database, please feel free to request thro
Schedule a call with Embedchain founder
</Card>
</CardGroup>
</Tip>
+12 -3
View File
@@ -11,6 +11,11 @@ app:
config:
id: 'full-stack-app'
chunker:
chunk_size: 100
chunk_overlap: 20
length_function: 'len'
llm:
provider: openai
config:
@@ -49,7 +54,11 @@ Alright, let's dive into what each key means in the yaml config above:
1. `app` Section:
- `config`:
- `id` (String): The ID or name of your full-stack application.
2. `llm` Section:
2. `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.
3. `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).
- `model` (String): The specific model being used, 'gpt-3.5-turbo'.
- `config`:
@@ -59,13 +68,13 @@ Alright, let's dive into what each key means in the yaml config above:
- `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.
- `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.
3. `vectordb` Section:
4. `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).
- `config`:
- `collection_name` (String): The initial collection name for the database, set to 'full-stack-app'.
- `dir` (String): The directory for the database, set to 'db'.
- `allow_reset` (Boolean): Indicates whether resetting the database is allowed, set to true.
4. `embedder` Section:
5. `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).
- `config`:
- `model` (String): The specific model used for text embedding, 'text-embedding-ada-002'.
+6 -6
View File
@@ -24,7 +24,7 @@ Once you have obtained the key, you can use it like this:
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
@@ -52,7 +52,7 @@ To use Azure OpenAI embedding model, you have to set some of the azure openai re
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["OPENAI_API_TYPE"] = "azure"
os.environ["OPENAI_API_BASE"] = "https://xxx.openai.azure.com/"
@@ -90,7 +90,7 @@ GPT4All supports generating high quality embeddings of arbitrary length document
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -100,7 +100,7 @@ app = App.from_config(yaml_path="config.yaml")
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
@@ -119,7 +119,7 @@ Hugging Face supports generating embeddings of arbitrary length documents of tex
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -150,7 +150,7 @@ Embedchain supports Google's VertexAI embeddings model through a simple interfac
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load embedding model configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml")
+11 -11
View File
@@ -26,7 +26,7 @@ Once you have obtained the key, you can use it like this:
```python
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
@@ -41,7 +41,7 @@ If you are looking to configure the different parameters of the LLM, you can do
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
@@ -71,7 +71,7 @@ To use Azure OpenAI model, you have to set some of the azure openai related envi
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["OPENAI_API_TYPE"] = "azure"
os.environ["OPENAI_API_BASE"] = "https://xxx.openai.azure.com/"
@@ -110,7 +110,7 @@ To use anthropic's model, please set the `ANTHROPIC_API_KEY` which you find on t
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["ANTHROPIC_API_KEY"] = "xxx"
@@ -147,7 +147,7 @@ Once you have the API key, you are all set to use it with Embedchain.
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["COHERE_API_KEY"] = "xxx"
@@ -180,7 +180,7 @@ GPT4all is a free-to-use, locally running, privacy-aware chatbot. No GPU or inte
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -190,7 +190,7 @@ app = App.from_config(yaml_path="config.yaml")
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
@@ -212,7 +212,7 @@ Once you have the key, load the app using the config yaml file:
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["JINACHAT_API_KEY"] = "xxx"
# load llm configuration from config.yaml file
@@ -248,7 +248,7 @@ Once you have the token, load the app using the config yaml file:
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["HUGGINGFACE_ACCESS_TOKEN"] = "xxx"
@@ -278,7 +278,7 @@ Once you have the token, load the app using the config yaml file:
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["REPLICATE_API_TOKEN"] = "xxx"
@@ -305,7 +305,7 @@ Setup Google Cloud Platform application credentials by following the instruction
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load llm configuration from config.yaml file
app = App.from_config(yaml_path="config.yaml")
+8 -8
View File
@@ -22,7 +22,7 @@ Utilizing a vector database alongside Embedchain is a seamless process. All you
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load chroma configuration from yaml file
app = App.from_config(yaml_path="config1.yaml")
@@ -61,7 +61,7 @@ pip install --upgrade 'embedchain[elasticsearch]'
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load elasticsearch configuration from yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -89,7 +89,7 @@ pip install --upgrade 'embedchain[opensearch]'
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load opensearch configuration from yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -125,7 +125,7 @@ Set the Zilliz environment variables `ZILLIZ_CLOUD_URI` and `ZILLIZ_CLOUD_TOKEN`
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['ZILLIZ_CLOUD_URI'] = 'https://xxx.zillizcloud.com'
os.environ['ZILLIZ_CLOUD_TOKEN'] = 'xxx'
@@ -138,7 +138,7 @@ app = App.from_config(yaml_path="config.yaml")
vectordb:
provider: zilliz
config:
collection_name: 'zilliz-app'
collection_name: 'zilliz_app'
uri: https://xxxx.api.gcp-region.zillizcloud.com
token: xxx
vector_dim: 1536
@@ -164,7 +164,7 @@ In order to use Pinecone as vector database, set the environment variables `PINE
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load pinecone configuration from yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -187,7 +187,7 @@ In order to use Qdrant as a vector database, set the environment variables `QDRA
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load qdrant configuration from yaml file
app = App.from_config(yaml_path="config.yaml")
@@ -207,7 +207,7 @@ In order to use Weaviate as a vector database, set the environment variables `WE
<CodeGroup>
```python main.py
from embedchain import App
from embedchain import Pipeline as App
# load weaviate configuration from yaml file
app = App.from_config(yaml_path="config.yaml")
+1 -1
View File
@@ -5,7 +5,7 @@ title: '📊 CSV'
To add any csv file, use the data_type as `csv`. `csv` allows remote urls and conventional file paths. Headers are included for each line, so if you have an `age` column, `18` will be added as `age: 18`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add('https://people.sc.fsu.edu/~jburkardt/data/csv/airtravel.csv', data_type="csv")
+2 -2
View File
@@ -35,7 +35,7 @@ Default behavior is to create a persistent vector db in the directory **./db**.
Create a local index:
```python
from embedchain import App
from embedchain import Pipeline as App
naval_chat_bot = App()
naval_chat_bot.add("https://www.youtube.com/watch?v=3qHkcs3kG44")
@@ -45,7 +45,7 @@ naval_chat_bot.add("https://navalmanack.s3.amazonaws.com/Eric-Jorgenson_The-Alma
You can reuse the local index with the same code, but without adding new documents:
```python
from embedchain import App
from embedchain import Pipeline as 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?"))
+1 -1
View File
@@ -5,7 +5,7 @@ title: '📚🌐 Code documentation'
To add any code documentation website as a loader, use the data_type as `docs_site`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add("https://docs.embedchain.ai/", data_type="docs_site")
+1 -1
View File
@@ -7,7 +7,7 @@ title: '📄 Docx file'
To add any doc/docx file, use the data_type as `docx`. `docx` allows remote urls and conventional file paths. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add('https://example.com/content/intro.docx', data_type="docx")
+4 -5
View File
@@ -24,12 +24,11 @@ To use this you need to save `credentials.json` in the directory from where you
12. Put the `.json` file in your current directory and rename it to `credentials.json`
```python
import os
from embedchain.apps.app import App
from embedchain.models.data_type import DataType
from embedchain import Pipeline as App
app = App()
query = "to: me label:inbox"
app.add(query, data_type=DataType.GMAIL)
gmail_filter = "to: me label:inbox"
app.add(gmail_filter, data_type="gmail")
app.query("Summarize my email conversations")
```
+24 -16
View File
@@ -2,35 +2,43 @@
title: '📃 JSON'
---
To add any json file, use the data_type as `json`. `json` allows remote urls and conventional file paths. Headers are included for each line, so if you have an `age` column, `18` will be added as `age: 18`. Eg:
To add any json file, use the data_type as `json`. Headers are included for each line, so for example if you have a json like `{"age": 18}`, then it will be added as `age: 18`.
```python
import os
Here are the supported sources for loading `json`:
from embedchain.apps.app import App
```
1. URL - valid url to json file that ends with ".json" extension.
2. Local file - valid url to local json file that ends with ".json" extension.
3. String - valid json string (e.g. - app.add('{"foo": "bar"}'))
```
os.environ["OPENAI_API_KEY"] = "openai_api_key"
<Tip>
If you would like to add other data structures (e.g. list, dict etc.), convert it to a valid json first using `json.dumps()` function.
</Tip>
## Example
<CodeGroup>
```python python
from embedchain import Pipeline as App
app = App()
response = app.query("What is the net worth of Elon Musk as of October 2023?")
# Add json file
app.add("temp.json")
print(response)
"I'm sorry, but I don't have access to real-time information or future predictions. Therefore, I don't know the net worth of Elon Musk as of October 2023."
source_id = app.add("temp.json")
response = app.query("What is the net worth of Elon Musk as of October 2023?")
print(response)
"As of October 2023, Elon Musk's net worth is $255.2 billion."
app.query("What is the net worth of Elon Musk as of October 2023?")
# As of October 2023, Elon Musk's net worth is $255.2 billion.
```
```temp.json
```json temp.json
{
"question": "What is your net worth, Elon Musk?",
"answer": "As of October 2023, Elon Musk's net worth is $255.2 billion, making him one of the wealthiest individuals in the world."
}
```
</CodeGroup>
+1 -1
View File
@@ -5,7 +5,7 @@ title: '📝 Mdx file'
To add any `.mdx` file to your app, use the data_type (first argument to `.add()` method) as `mdx`. Note that this supports support mdx file present on machine, so this should be a file path. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add('path/to/file.mdx', data_type='mdx')
+1 -1
View File
@@ -8,7 +8,7 @@ To load a notion page, use the data_type as `notion`. Since it is hard to automa
The next argument must **end** with the `notion page id`. The id is a 32-character string. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+6 -7
View File
@@ -2,13 +2,10 @@
title: 🙌 OpenAPI
---
To add any OpenAPI spec yaml file (currently the json file will be detected as JSON data type), use the data_type as 'openapi'. 'openapi' allows remote urls and conventional file paths. Headers are included for each line, so if you have an `age` column, `18` will be added as `age: 18`. Eg:
To add any OpenAPI spec yaml file (currently the json file will be detected as JSON data type), use the data_type as 'openapi'. 'openapi' allows remote urls and conventional file paths.
```python
from embedchain.apps.app import App
import os
os.environ["OPENAI_API_KEY"] = "sk-xxx"
from embedchain import Pipeline as App
app = App()
@@ -16,8 +13,10 @@ app.add("https://github.com/openai/openai-openapi/blob/master/openapi.yaml", dat
# Or add using the local file path
# app.add("configs/openai_openapi.yaml", data_type="openapi")
response = app.query("What can OpenAI API endpoint do? Can you list the things it can learn from?")
app.query("What can OpenAI API endpoint do? Can you list the things it can learn from?")
# Answer: The OpenAI API endpoint allows users to interact with OpenAI's models and perform various tasks such as generating text, answering questions, summarizing documents, translating languages, and more. The specific capabilities and tasks that the API can learn from may vary depending on the models and features provided by OpenAI. For more detailed information, it is recommended to refer to the OpenAI API documentation at https://platform.openai.com/docs/api-reference.
```
NOTE: The yaml file added to the App must have the required OpenAPI fields otherwise the adding OpenAPI spec will fail. Please refer to [OpenAPI Spec Doc](https://spec.openapis.org/oas/v3.1.0)
<Note>
The yaml file added to the App must have the required OpenAPI fields otherwise the adding OpenAPI spec will fail. Please refer to [OpenAPI Spec Doc](https://spec.openapis.org/oas/v3.1.0)
</Note>
+1
View File
@@ -20,6 +20,7 @@ Embedchain comes with built-in support for various data sources. We handle the c
<Card title="🙌 OpenAPI" href="/data-sources/openapi"></Card>
<Card title="🎥📺 youtube video" href="/data-sources/youtube-video"></Card>
<Card title="📬 Gmail" href="/data-sources/gmail"></Card>
<Card title="🐘 Postgres" href="/data-sources/postgres"></Card>
</CardGroup>
<br/ >
+1 -1
View File
@@ -5,7 +5,7 @@ title: '📰 PDF file'
To add any pdf file, use the data_type as `pdf_file`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+64
View File
@@ -0,0 +1,64 @@
---
title: '🐘 Postgres'
---
1. Setup the Postgres loader by configuring the postgres db.
```Python
from embedchain.loaders.postgres import PostgresLoader
config = {
"host": "host_address",
"port": "port_number",
"dbname": "database_name",
"user": "username",
"password": "password",
}
"""
config = {
"url": "your_postgres_url"
}
"""
postgres_loader = PostgresLoader(config=config)
```
You can either setup the loader by passing the postgresql url or by providing the config data.
For more details on how to setup with valid url and config, check postgres [documentation](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING:~:text=34.1.1.%C2%A0Connection%20Strings-,%23,-Several%20libpq%20functions).
NOTE: if you provide the `url` field in config, all other fields will be ignored.
2. Once you setup the loader, you can create an app and load data using the above postgres loader
```Python
import os
from embedchain.pipeline import Pipeline as App
os.environ["OPENAI_API_KEY"] = "sk-xxx"
app = App()
question = "What is Elon Musk's networth?"
response = app.query(question)
# Answer: As of September 2021, Elon Musk's net worth is estimated to be around $250 billion, making him one of the wealthiest individuals in the world. However, please note that net worth can fluctuate over time due to various factors such as stock market changes and business ventures.
app.add("SELECT * FROM table_name;", data_type='postgres', loader=postgres_loader)
# Adds `(1, 'What is your net worth, Elon Musk?', "As of October 2023, Elon Musk's net worth is $255.2 billion.")`
response = app.query(question)
# Answer: As of October 2023, Elon Musk's net worth is $255.2 billion.
```
NOTE: The `add` function of the app will accept any executable query to load data. DO NOT pass the `CREATE`, `INSERT` queries in `add` function as they will result in not adding any data, so it is pointless.
3. We automatically create a chunker to chunk your postgres data, however if you wish to provide your own chunker class. Here is how you can do that:
```Python
from embedchain.chunkers.postgres import PostgresChunker
from embedchain.config.add_config import ChunkerConfig
postgres_chunker_config = ChunkerConfig(chunk_size=1000, chunk_overlap=0, length_function=len)
postgres_chunker = PostgresChunker(config=postgres_chunker_config)
app.add("SELECT * FROM table_name;", data_type='postgres', loader=postgres_loader, chunker=postgres_chunker)
```
+1 -1
View File
@@ -5,7 +5,7 @@ title: '❓💬 Queston and answer pair'
QnA pair is a local data type. To supply your own QnA pair, use the data_type as `qna_pair` and enter a tuple. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+1 -1
View File
@@ -5,7 +5,7 @@ title: '🗺️ Sitemap'
Add all web pages from an xml-sitemap. Filters non-text files. Use the data_type as `sitemap`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+1 -1
View File
@@ -7,7 +7,7 @@ title: '📝 Text'
Text is a local data type. To supply your own text, use the data_type as `text` and enter a string. The text is not processed, this can be very versatile. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+1 -1
View File
@@ -5,7 +5,7 @@ title: '🌐📄 Web page'
To add any web page, use the data_type as `web_page`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+1 -1
View File
@@ -7,7 +7,7 @@ title: '🧾 XML file'
To add any xml file, use the data_type as `xml`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
+1 -1
View File
@@ -6,7 +6,7 @@ title: '🎥📺 Youtube video'
To add any youtube video to your app, use the data_type (first argument to `.add()` method) as `youtube_video`. Eg:
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add('a_valid_youtube_url_here', data_type='youtube_video')
-93
View File
@@ -1,93 +0,0 @@
---
title: '🌍 API Server'
---
The API server example can be found [here](https://github.com/embedchain/embedchain/tree/main/examples/api_server).
It is a Flask based server that integrates the `embedchain` package, offering endpoints to add, query, and chat to engage in conversations with a chatbot using JSON requests.
### 🐳 Docker Setup
- Open variables.env, and edit it to add your 🔑 `OPENAI_API_KEY`.
- To setup your api server using docker, run the following command inside this folder using your terminal.
```bash
docker-compose up --build
```
📝 Note: The build command might take a while to install all the packages depending on your system resources.
### 🚀 Usage Instructions
- Your api server is running on [http://localhost:5000/](http://localhost:5000/)
- To use the api server, make an api call to the endpoints `/add`, `/query` and `/chat` using the json formats discussed below.
- To add data sources to the bot (/add):
```json
// Request
{
"data_type": "your_data_type_here",
"url_or_text": "your_url_or_text_here"
}
// Response
{
"data": "Added data_type: url_or_text"
}
```
- To ask queries from the bot (/query):
```json
// Request
{
"question": "your_question_here"
}
// Response
{
"data": "your_answer_here"
}
```
- To chat with the bot (/chat):
```json
// Request
{
"question": "your_question_here"
}
// Response
{
"data": "your_answer_here"
}
```
### 📡 Curl Call Formats
- To add data sources to the bot (/add):
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"data_type": "your_data_type_here",
"url_or_text": "your_url_or_text_here"
}' \
http://localhost:5000/add
```
- To ask queries from the bot (/query):
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"question": "your_question_here"
}' \
http://localhost:5000/query
```
- To chat with the bot (/chat):
```bash
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"question": "your_question_here"
}' \
http://localhost:5000/chat
```
🎉 Happy Chatting! 🎉
+28 -20
View File
@@ -1,5 +1,5 @@
---
title: '🤖 Discord Bot'
title: "🤖 Discord Bot"
---
### 🔑 Keys Setup
@@ -12,9 +12,11 @@ title: '🤖 Discord Bot'
- On the left sidebar, click on `OAuth2` and go to `General`.
- Set `Authorization Method` to `In-app Authorization`. Under `Scopes` select `bot`.
- Under `Bot Permissions` allow the following and then click on `Save Changes`.
```text
Send Messages (under Text Permissions)
```
- Now under `OAuth2` and go to `URL Generator`. Under `Scopes` select `bot`.
- Under `Bot Permissions` set the same permissions as above.
- Now scroll down and copy the `Generated URL`. Paste it in a browser window and select the Server where you want to add the bot.
@@ -23,40 +25,46 @@ Send Messages (under Text Permissions)
### Take the bot online
1. Install embedchain python package:
<Tabs>
<Tab title="docker">
```bash
docker run --name discord-bot -e OPENAI_API_KEY=sk-xxx -e DISCORD_BOT_TOKEN=xxx -p 8080:8080 embedchain/discord-bot:latest
```
</Tab>
<Tab title="python">
```bash
pip install --upgrade "embedchain[discord]"
```bash
pip install --upgrade "embedchain[discord]"
```
python -m embedchain.bots.discord
2. Launch your Discord bot:
```bash
python -m embedchain.bots.discord
```
If you prefer to see the question and not only the answer, run it with
```bash
python -m embedchain.bots.discord --include-question
```
# or if you prefer to see the question and not only the answer, run it with
python -m embedchain.bots.discord --include-question
```
</Tab>
</Tabs>
### 🚀 Usage Instructions
- Go to the server where you have added your bot.
![Slash commands interaction with bot](https://github.com/embedchain/embedchain/assets/73601258/bf1414e3-d408-4863-b0d2-ef382a76467e)
- You can add data sources to the bot using the slash command:
```text
/add <data_type> <url_or_text>
/ec add <data_type> <url_or_text>
```
- You can ask your queries from the bot using the slash command:
```text
/query <question>
/ec query <question>
```
- You can chat with the bot using the slash command:
```text
/chat <question>
/ec chat <question>
```
📝 Note: To use the bot privately, you can message the bot directly by right clicking the bot and selecting `Message`.
🎉 Happy Chatting! 🎉
+33 -2
View File
@@ -8,14 +8,45 @@ This guide will help you setup the full stack app on your local machine.
### 🐳 Docker Setup
- To setup full stack app using docker, run the following command inside this folder using your terminal.
- Create a `docker-compose.yml` file and paste the following code in it.
```yaml
version: "3.9"
services:
backend:
container_name: embedchain-backend
restart: unless-stopped
build:
context: backend
dockerfile: Dockerfile
image: embedchain/backend
ports:
- "8000:8000"
frontend:
container_name: embedchain-frontend
restart: unless-stopped
build:
context: frontend
dockerfile: Dockerfile
image: embedchain/frontend
ports:
- "3000:3000"
depends_on:
- "backend"
```
- Run the following command,
```bash
docker-compose up --build
docker-compose up
```
📝 Note: The build command might take a while to install all the packages depending on your system resources.
![Fullstack App](https://github.com/embedchain/embedchain/assets/73601258/c7c04bbb-9be7-4669-a6af-039e7e972a13)
### 🚀 Usage Instructions
- Go to [http://localhost:3000/](http://localhost:3000/) in your browser to view the dashboard.
+29 -4
View File
@@ -1,21 +1,46 @@
---
title: '📱 Telegram Bot'
title: "📱 Telegram Bot"
---
### 🖼️ Template Setup
- Fork [this](https://replit.com/@taranjeetio/EC-Telegram-Bot-Template?v=1#README.md) replit template.
- Set your `OPENAI_API_KEY` in Secrets.
- Open the Telegram app and search for the `BotFather` user.
- Start a chat with BotFather and use the `/newbot` command to create a new bot.
- Follow the instructions to choose a name and username for your bot.
- Once the bot is created, BotFather will provide you with a unique token for your bot.
- Set this token as `TELEGRAM_BOT_TOKEN` in Secrets.
<Tabs>
<Tab title="docker">
```bash
docker run --name telegram-bot -e OPENAI_API_KEY=sk-xxx -e TELEGRAM_BOT_TOKEN=xxx -p 8000:8000 embedchain/telegram-bot
```
<Note>
If you wish to use **Docker**, you would need to host your bot on a server.
You can use [ngrok](https://ngrok.com/) to expose your localhost to the
internet and then set the webhook using the ngrok URL.
</Note>
</Tab>
<Tab title="replit">
<Card>
Fork <ins>**[this](https://replit.com/@taranjeetio/EC-Telegram-Bot-Template?v=1#README.md)**</ins> replit template.
</Card>
- Set your `OPENAI_API_KEY` in Secrets.
- Set the unique token as `TELEGRAM_BOT_TOKEN` in Secrets.
</Tab>
</Tabs>
- Click on `Run` in the replit container and a URL will get generated for your bot.
- Now set your webhook by running the following link in your browser:
```url
https://api.telegram.org/bot<Your_Telegram_Bot_Token>/setWebhook?url=<Replit_Generated_URL>
```
- When you get a successful response in your browser, your bot is ready to be used.
### 🚀 Usage Instructions
+12 -3
View File
@@ -12,10 +12,19 @@ pip install --upgrade embedchain
2. Launch your WhatsApp bot:
<Tabs>
<Tab title="docker">
```bash
docker run --name whatsapp-bot -e OPENAI_API_KEY=sk-xxx -p 8000:8000 embedchain/whatsapp-bot
```
</Tab>
<Tab title="python">
```bash
python -m embedchain.bots.whatsapp --port 5000
```
</Tab>
</Tabs>
```bash
python -m embedchain.bots.whatsapp --port 5000
```
If your bot needs to be accessible online, use your machine's public IP or DNS. Otherwise, employ a proxy server like [ngrok](https://ngrok.com/) to make your local bot accessible.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

+35 -3
View File
@@ -3,13 +3,45 @@ title: ❓ FAQs
description: 'Collections of all the frequently asked questions'
---
#### Does Embedchain support OpenAI's Assistant APIs?
Yes, it does. Please refer to the [OpenAI Assistant docs page](/get-started/openai-assistant).
#### How to use `gpt-4-turbo` model released on OpenAI DevDay?
<CodeGroup>
```python main.py
import os
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
# load llm configuration from gpt4_turbo.yaml file
app = App.from_config(yaml_path="gpt4_turbo.yaml")
```
```yaml gpt4_turbo.yaml
llm:
provider: openai
config:
model: 'gpt-4-turbo'
temperature: 0.5
max_tokens: 1000
top_p: 1
stream: false
```
</CodeGroup>
#### How to use GPT-4 as the LLM model?
<CodeGroup>
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
@@ -36,7 +68,7 @@ llm:
```python main.py
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ['OPENAI_API_KEY'] = 'xxx'
@@ -48,7 +80,7 @@ app = App.from_config(yaml_path="opensource.yaml")
llm:
provider: gpt4all
config:
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
model: 'orca-mini-3b-gguf2-q4_0.gguf'
temperature: 0.5
max_tokens: 1000
top_p: 1
+92 -16
View File
@@ -3,30 +3,106 @@ title: 📚 Introduction
description: '📝 Embedchain is a Data Platform for LLMs - load, index, retrieve, and sync any unstructured data'
---
## 🤔 What is Embedchain?
## 🌐 What is Embedchain?
Embedchain abstracts the entire process of loading data, chunking it, creating embeddings, and storing it in a vector database.
Embedchain simplifies data handling by automatically processing unstructured data, breaking it into chunks, generating embeddings, and storing it in a vector database.
You can add data from different data sources using the `.add()` method. Then, simply use the `.query()` method to find answers from the added datasets.
Through various APIs, you can obtain contextual information for queries, find answers to specific questions, and engage in chat conversations using your data.
## 🔍 Search
If you want to create a Naval Ravikant bot with a YouTube video, a book in PDF format, two blog posts, and a question and answer pair, all you need to do is add the respective links. Embedchain will take care of the rest, creating a bot for you.
Embedchain lets you get most relevant context by doing semantic search over your data sources for a provided query. See the example below:
```python
from embedchain import App
from embedchain import Pipeline as App
naval_bot = App()
# Add online data
naval_bot.add("https://www.youtube.com/watch?v=3qHkcs3kG44")
naval_bot.add("https://navalmanack.s3.amazonaws.com/Eric-Jorgenson_The-Almanack-of-Naval-Ravikant_Final.pdf")
naval_bot.add("https://nav.al/feedback")
naval_bot.add("https://nav.al/agi")
naval_bot.add("The Meanings of Life", 'text', metadata={'chapter': 'philosphy'})
# Initialize app
app = App()
# Add local resources
naval_bot.add(("Who is Naval Ravikant?", "Naval Ravikant is an Indian-American entrepreneur and investor."))
# Add data source
app.add("https://www.forbes.com/profile/elon-musk")
naval_bot.query("What unique capacity does Naval argue humans possess when it comes to understanding explanations or concepts?")
# Answer: Naval argues that humans possess the unique capacity to understand explanations or concepts to the maximum extent possible in this physical reality.
# Get relevant context using semantic search
context = app.search("What is the net worth of Elon?", num_documents=2)
print(context)
# Context:
# [
# {
# 'context': 'Elon Musk PROFILEElon MuskCEO, Tesla$221.9BReal Time Net Worthas of 10/29/23Reflects change since 5 pm ET of prior trading day. 1 in the world todayPhoto by Martin Schoeller for ForbesAbout Elon MuskElon Musk cofounded six companies, including electric car maker Tesla, rocket producer SpaceX and tunneling startup Boring Company.He owns about 21% of Tesla between stock and options, but has pledged more than half his shares as collateral for personal loans of up to $3.5 billion.SpaceX, founded in',
# 'source': 'https://www.forbes.com/profile/elon-musk',
# 'document_id': 'some_document_id'
# },
# {
# 'context': 'company, which is now called X.Wealth HistoryHOVER TO REVEAL NET WORTH BY YEARForbes Lists 1Forbes 400 (2023)The Richest Person In Every State (2023) 2Billionaires (2023) 1Innovative Leaders (2019) 25Powerful People (2018) 12Richest In Tech (2017)Global Game Changers (2016)More ListsPersonal StatsAge52Source of WealthTesla, SpaceX, Self MadeSelf-Made Score8Philanthropy Score1ResidenceAustin, TexasCitizenshipUnited StatesMarital StatusSingleChildren11EducationBachelor of Arts/Science, University',
# 'source': 'https://www.forbes.com/profile/elon-musk',
# 'document_id': 'some_document_id'
# }
# ]
```
## ❓Query
Embedchain empowers developers to ask questions and receive relevant answers through a user-friendly query API. Refer to the following example to learn how to utilize the query API:
```python
from embedchain import Pipeline as App
# Initialize app
app = App()
# Add data source
app.add("https://www.forbes.com/profile/elon-musk")
# Get relevant answer for your query
answer = app.query("What is the net worth of Elon?")
print(answer)
# Answer: The net worth of Elon Musk is $221.9 billion.
```
## 💬 Chat
Embedchain allows easy chatting over your data sources using a user-friendly chat API. Check out the example below to understand how to use the chat API:
```python
from embedchain import Pipeline as App
# Initialize app
app = App()
# Add data source
app.add("https://www.forbes.com/profile/elon-musk")
# Chat on your data using `.chat()`
answer = app.chat("How much did Elon pay for Twitter?")
print(answer)
# Answer: Elon Musk paid $44 billion for Twitter.
```
## 🚀 Deploy
Embedchain enables developers to deploy their LLM-powered apps in production using the Embedchain platform. The platform offers free access to context on your data through its REST API. Once the pipeline is deployed, you can update your data sources anytime after deployment.
See the example below on how to use the deploy API:
```python
from embedchain import Pipeline as App
# Initialize app
app = App()
# Add data source
app.add("https://www.forbes.com/profile/elon-musk")
# Deploy your pipeline to Embedchain Platform
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.
```
## 🚀 How it works?
+95
View File
@@ -0,0 +1,95 @@
---
title: '🤖 OpenAI Assistant'
---
<img src="https://blogs.swarthmore.edu/its/wp-content/uploads/2022/05/openai.jpg" align="center" width="500" alt="OpenAI Logo"/>
Embedchain now supports [OpenAI Assistants API](https://platform.openai.com/docs/assistants/overview) which allows you to build AI assistants within your own applications. An Assistant has instructions and can leverage models, tools, and knowledge to respond to user queries.
At a high level, an integration of the Assistants API has the following flow:
1. Create an Assistant in the API by defining custom instructions and picking a model
2. Create a Thread when a user starts a conversation
3. Add Messages to the Thread as the user ask questions
4. Run the Assistant on the Thread to trigger responses. This automatically calls the relevant tools.
Creating an OpenAI Assistant using Embedchain is very simple 3 step process.
## Step 1: Create OpenAI Assistant
Make sure that you have `OPENAI_API_KEY` set in the environment variable.
```python Initialize
from embedchain.store.assistants import OpenAIAssistant
assistant = OpenAIAssistant(
name="OpenAI DevDay Assistant",
instructions="You are an organizer of OpenAI DevDay",
)
```
If you want to use the existing assistant, you can do something like this:
```python Initialize
# Load an assistant and create a new thread
assistant = OpenAIAssistant(assistant_id="asst_xxx")
# Load a specific thread for an assistant
assistant = OpenAIAssistant(assistant_id="asst_xxx", thread_id="thread_xxx")
```
### Arguments
<ResponseField name="name" type="string">
Name for your AI assistant
</ResponseField>
<ResponseField name="instructions" type="string">
how the Assistant and model should behave or respond
</ResponseField>
<ResponseField name="assistant_id" type="string">
Load existing OpenAI Assistant. If you pass this, you don't have to pass other arguments.
</ResponseField>
<ResponseField name="thread_id" type="string">
Existing OpenAI thread id if exists
</ResponseField>
<ResponseField name="model" type="str" default="gpt-4-1106-preview">
OpenAI model to use
</ResponseField>
<ResponseField name="tools" type="list">
OpenAI tools to use. Default set to `[{"type": "retrieval"}]`
</ResponseField>
<ResponseField name="data_sources" type="list" default="[]">
Add data sources to your assistant. You can add in the following format: `[{"source": "https://example.com", "data_type": "web_page"}]`
</ResponseField>
<ResponseField name="telemetry" type="boolean" default="True">
Anonymous telemetry (doesn't collect any user information or user's files). Used to improve the Embedchain package utilization. Default is `True`.
</ResponseField>
## Step-2: Add data to thread
You can add any custom data source that is supported by Embedchain. Else, you can directly pass the file path on your local system and Embedchain propagates it to OpenAI Assistant.
```python Add data
assistant.add("/path/to/file.pdf")
assistant.add("https://www.youtube.com/watch?v=U9mJuUkhUzk")
assistant.add("https://openai.com/blog/new-models-and-developer-products-announced-at-devday")
```
## Step-3: Chat with your Assistant
```python Chat
assistant.chat("How much OpenAI credits were offered to attendees during OpenAI DevDay?")
# Response: 'Every attendee of OpenAI DevDay 2023 was offered $500 in OpenAI credits.'
```
You can try it out yourself using the following Google Colab notebook:
<a href="https://colab.research.google.com/drive/1BKlXZYSl6AFRgiHZ5XIzXrXC_24kDYHQ?usp=sharing">
<img src="https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667" alt="Open in Colab" />
</a>
+39 -12
View File
@@ -11,28 +11,45 @@ Install embedchain python package:
pip install embedchain
```
<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.
</Tip>
Creating an app involves 3 steps:
<Steps>
<Step title="⚙️ Import app instance">
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
```
</Step>
<Step title="🗃️ Add data sources">
```python
# Add different data sources
elon_bot.add("https://en.wikipedia.org/wiki/Elon_Musk")
elon_bot.add("https://www.forbes.com/profile/elon-musk")
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.
# elon_bot.add("/path/to/file.pdf")
# app.add("/path/to/file.pdf")
```
</Step>
<Step title="💬 Query or chat on your data and get answers">
<Step title="💬 Query or chat or search context on your data">
```python
elon_bot.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.
```
</Step>
<Step title="🚀 (Optional) Deploy your pipeline to Embedchain Platform">
```python
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.
```
</Step>
</Steps>
@@ -41,18 +58,28 @@ Putting it together, you can run your first app using the following code. Make s
```python
import os
from embedchain import App
from embedchain import Pipeline as App
os.environ["OPENAI_API_KEY"] = "xxx"
elon_bot = App()
app = App()
# Add different data sources
elon_bot.add("https://en.wikipedia.org/wiki/Elon_Musk")
elon_bot.add("https://www.forbes.com/profile/elon-musk")
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.
# elon_bot.add("/path/to/file.pdf")
# app.add("/path/to/file.pdf")
response = elon_bot.query("What is the net worth of Elon Musk today?")
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.
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

+1 -1
View File
@@ -39,7 +39,7 @@ os.environ['LANGCHAIN_PROJECT] = <your-project>
```python
from embedchain import App
from embedchain import Pipeline as App
app = App()
app.add("https://en.wikipedia.org/wiki/Elon_Musk")
+23 -14
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

+23 -14
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

+89 -34
View File
@@ -3,40 +3,70 @@
"name": "Embedchain",
"logo": {
"dark": "/logo/dark.svg",
"light": "/logo/light.svg"
"light": "/logo/light.svg",
"href": "https://embedchain.ai/"
},
"favicon": "/favicon.png",
"colors": {
"primary": "#12A7D3",
"light": "#81D7F7",
"dark": "#004E7A"
"primary": "#3B2FC9",
"light": "#6673FF",
"dark": "#3B2FC9",
"background": {
"dark": "#0f1117",
"light": "#fff"
}
},
"modeToggle": {
"default": "dark"
},
"openapi": ["/rest-api.json"],
"metadata": {
"og:image": "/images/og.png",
"twitter:site": "@embedchain"
},
"anchors": [
{
"name": "Embedchain Platform",
"icon": "tv",
"url": "https://app.embedchain.ai/"
},
{
"name": "Join our slack",
"icon": "slack",
"url": "https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw"
}
],
"topbarLinks": [
{
"name": "Twitter",
"url": "https://twitter.com/embedchain"
},
{
"name":"Slack",
"url":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw"
},
{
"name": "Discord",
"url": "https://discord.gg/6PzXDgEjG5"
"name": "Create account",
"url": "https://app.embedchain.ai/login/"
}
],
"topbarCtaButton": {
"name": "GitHub",
"url": "https://embedchain.ai"
"name": "Get started",
"url": "https://app.embedchain.ai"
},
"primaryTab": {
"name": "Docs"
},
"navigation": [
{
"group": "Get started",
"pages": ["get-started/quickstart", "get-started/introduction", "get-started/faq", "get-started/examples"]
"pages": [
"get-started/quickstart",
"get-started/introduction",
"get-started/openai-assistant",
"get-started/faq",
"get-started/examples"
]
},
{
"group": "Components",
"pages": ["components/llms", "components/embedding-models", "components/vector-databases"]
"pages": [
"components/llms",
"components/embedding-models",
"components/vector-databases"
]
},
{
"group": "Data sources",
@@ -68,19 +98,33 @@
"pages": ["advanced/configuration"]
},
{
"group": "Examples",
"pages": ["examples/full_stack", "examples/api_server", "examples/discord_bot", "examples/slack_bot", "examples/telegram_bot", "examples/whatsapp_bot", "examples/poe_bot"]
"group": "REST API",
"pages": [
"rest-api/getting-started",
"rest-api/create",
"rest-api/get-all-apps",
"rest-api/add-data",
"rest-api/get-data",
"rest-api/query",
"rest-api/deploy",
"rest-api/delete",
"rest-api/check-status"
]
},
{
"group": "Pipelines",
"pages": ["pipelines/quickstart"]
"group": "Use Cases",
"pages": [
"examples/full_stack",
"examples/discord_bot",
"examples/slack_bot",
"examples/telegram_bot",
"examples/whatsapp_bot",
"examples/poe_bot"
]
},
{
"group": "Community",
"pages": [
"community/connect-with-us",
"community/showcase"
]
"pages": ["community/connect-with-us", "community/showcase"]
},
{
"group": "Integrations",
@@ -98,22 +142,33 @@
},
{
"group": "Product",
"pages": [
"product/release-notes"
]
"pages": ["product/release-notes"]
}
],
"footerSocials": {
"website": "https://embedchain.ai",
"github": "https://github.com/embedchain/embedchain",
"slack":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw",
"slack": "https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw",
"discord": "https://discord.gg/6PzXDgEjG5",
"twitter": "https://twitter.com/embedchain",
"linkedin": "https://www.linkedin.com/company/embedchain"
},
"backgroundImage": "/background.png",
"isWhiteLabeled": true,
"feedback.thumbsRating": true
"analytics": {
"posthog": {
"apiKey": "phc_PHQDA5KwztijnSojsxJ2c1DuJd52QCzJzT2xnSGvjN2",
"apiHost": "https://app.embedchain.ai/ingest"
}
},
"feedback": {
"suggestEdit": true,
"raiseIssue": true,
"thumbsRating": true
},
"search": {
"prompt": "✨ Search embedchain docs..."
},
"api": {
"baseUrl": "http://localhost:8080"
}
}
-44
View File
@@ -1,44 +0,0 @@
---
title: '🚀 Pipelines'
description: '💡 Start building LLM powered data pipelines in 1 minute'
---
Embedchain lets you build data pipelines on your own data sources and deploy it in production in less than a minute. It can load, index, retrieve, and sync any unstructured data.
Install embedchain python package:
```bash
pip install embedchain
```
Creating a pipeline involves 3 steps:
<Steps>
<Step title="⚙️ Import pipeline instance">
```python
from embedchain import Pipeline
p = Pipeline(name="Elon Musk")
```
</Step>
<Step title="🗃️ Add data sources">
```python
# Add different data sources
p.add("https://en.wikipedia.org/wiki/Elon_Musk")
p.add("https://www.forbes.com/profile/elon-musk")
# You can also add local data sources such as pdf, csv files etc.
# p.add("/path/to/file.pdf")
```
</Step>
<Step title="💬 Deploy your pipeline to Embedchain platform">
```python
p.deploy()
```
</Step>
</Steps>
That's it. Now, head to the [Embedchain platform](https://app.embedchain.ai) and your pipeline is available there. Make sure to set the `OPENAI_API_KEY` 🔑 environment variable in the code.
After you deploy your pipeline to Embedchain platform, you can still add more data sources and update the pipeline multiple times.
Here is a Google Colab notebook for you to get started: [![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/1YVXaBO4yqlHZY4ho67GCJ6aD4CHNiScD?usp=sharing)
+427
View File
@@ -0,0 +1,427 @@
{
"openapi": "3.1.0",
"info": {
"title": "Embedchain REST API",
"description": "This is the REST API for Embedchain.",
"license": {
"name": "Apache 2.0",
"url": "https://github.com/embedchain/embedchain/blob/main/LICENSE"
},
"version": "0.0.1"
},
"paths": {
"/ping": {
"get": {
"tags": ["Utility"],
"summary": "Check status",
"description": "Endpoint to check the status of the API",
"operationId": "check_status_ping_get",
"responses": {
"200": {
"description": "Successful Response",
"content": { "application/json": { "schema": {} } }
}
}
}
},
"/apps": {
"get": {
"tags": ["Apps"],
"summary": "Get all apps",
"description": "Get all applications",
"operationId": "get_all_apps_apps_get",
"responses": {
"200": {
"description": "Successful Response",
"content": { "application/json": { "schema": {} } }
}
}
}
},
"/create": {
"post": {
"tags": ["Apps"],
"summary": "Create app",
"description": "Create a new app using App ID",
"operationId": "create_app_using_default_config_create_post",
"parameters": [
{
"name": "app_id",
"in": "query",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/Body_create_app_using_default_config_create_post"
}
],
"title": "Body"
}
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/data": {
"get": {
"tags": ["Apps"],
"summary": "Get data",
"description": "Get all data sources for an app",
"operationId": "get_datasources_associated_with_app_id__app_id__data_get",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": { "application/json": { "schema": {} } }
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/add": {
"post": {
"tags": ["Apps"],
"summary": "Add data",
"description": "Add a data source to an app.",
"operationId": "add_datasource_to_an_app__app_id__add_post",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/SourceApp" }
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/query": {
"post": {
"tags": ["Apps"],
"summary": "Query app",
"description": "Query an app",
"operationId": "query_an_app__app_id__query_post",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/QueryApp" }
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/chat": {
"post": {
"tags": ["Apps"],
"summary": "Chat",
"description": "Chat with an app.\n\napp_id: The ID of the app. Use \"default\" for the default app.\n\nmessage: The message that you want to send to the app.",
"operationId": "chat_with_an_app__app_id__chat_post",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/MessageApp" }
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/deploy": {
"post": {
"tags": ["Apps"],
"summary": "Deploy App",
"description": "Deploy an existing app.",
"operationId": "deploy_app__app_id__deploy_post",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DeployAppRequest" }
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
},
"/{app_id}/delete": {
"delete": {
"tags": ["Apps"],
"summary": "Delete app",
"description": "Delete an existing app",
"operationId": "delete_app__app_id__delete_delete",
"parameters": [
{
"name": "app_id",
"in": "path",
"required": true,
"schema": { "type": "string", "title": "App Id" }
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/DefaultResponse" }
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
}
}
}
}
}
}
},
"components": {
"schemas": {
"Body_create_app_using_default_config_create_post": {
"properties": {
"config": { "type": "string", "format": "binary", "title": "Config" }
},
"type": "object",
"title": "Body_create_app_using_default_config_create_post"
},
"DefaultResponse": {
"properties": { "response": { "type": "string", "title": "Response" } },
"type": "object",
"required": ["response"],
"title": "DefaultResponse"
},
"DeployAppRequest": {
"properties": {
"api_key": {
"type": "string",
"title": "Api Key",
"description": "The Embedchain API key for app deployments. You get the api key on the Embedchain platform by visiting [https://app.embedchain.ai](https://app.embedchain.ai)",
"default": ""
}
},
"type": "object",
"title": "DeployAppRequest",
"example":{
"api_key":"ec-xxx"
}
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": { "$ref": "#/components/schemas/ValidationError" },
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"MessageApp": {
"properties": {
"message": {
"type": "string",
"title": "Message",
"description": "The message that you want to send to the App.",
"default": ""
}
},
"type": "object",
"title": "MessageApp"
},
"QueryApp": {
"properties": {
"query": {
"type": "string",
"title": "Query",
"description": "The query that you want to ask the App.",
"default": ""
}
},
"type": "object",
"title": "QueryApp",
"example":{
"query":"Who is Elon Musk?"
}
},
"SourceApp": {
"properties": {
"source": {
"type": "string",
"title": "Source",
"description": "The source that you want to add to the App.",
"default": ""
},
"data_type": {
"anyOf": [{ "type": "string" }, { "type": "null" }],
"title": "Data Type",
"description": "The type of data to add, remove it if you want Embedchain to detect it automatically.",
"default": ""
}
},
"type": "object",
"title": "SourceApp",
"example":{
"source":"https://en.wikipedia.org/wiki/Elon_Musk"
}
},
"ValidationError": {
"properties": {
"loc": {
"items": { "anyOf": [{ "type": "string" }, { "type": "integer" }] },
"type": "array",
"title": "Location"
},
"msg": { "type": "string", "title": "Message" },
"type": { "type": "string", "title": "Error Type" }
},
"type": "object",
"required": ["loc", "msg", "type"],
"title": "ValidationError"
}
}
}
}
+22
View File
@@ -0,0 +1,22 @@
---
openapi: post /{app_id}/add
---
<RequestExample>
```bash Request
curl --request POST \
--url http://localhost:8080/{app_id}/add \
-d "source=https://www.forbes.com/profile/elon-musk" \
-d "data_type=web_page"
```
</RequestExample>
<ResponseExample>
```json Response
{ "response": "fec7fe91e6b2d732938a2ec2e32bfe3f" }
```
</ResponseExample>
+3
View File
@@ -0,0 +1,3 @@
---
openapi: post /{app_id}/chat
---
+20
View File
@@ -0,0 +1,20 @@
---
openapi: get /ping
---
<RequestExample>
```bash Request
curl --request GET \
--url http://localhost:8080/ping
```
</RequestExample>
<ResponseExample>
```json Response
{ "ping": "pong" }
```
</ResponseExample>
+95
View File
@@ -0,0 +1,95 @@
---
openapi: post /create
---
<RequestExample>
```bash Request
curl --request POST \
--url http://localhost:8080/create?app_id=app1 \
-F "config=@/path/to/config.yaml"
```
</RequestExample>
<ResponseExample>
```json Response
{ "response": "App created successfully. App ID: app1" }
```
</ResponseExample>
By default we will use the opensource **gpt4all** model to get started. You can also specify your own config by uploading a config YAML file.
For example, create a `config.yaml` file (adjust according to your requirements):
```yaml
app:
config:
id: "default-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.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
$context
Query: $query
Helpful Answer:
vectordb:
provider: chroma
config:
collection_name: "rest-api-app"
dir: db
allow_reset: true
embedder:
provider: openai
config:
model: "text-embedding-ada-002"
```
To learn more about custom configurations, check out the [custom configurations docs](https://docs.embedchain.ai/advanced/configuration). To explore more examples of config yamls for embedchain, visit [embedchain/configs](https://github.com/embedchain/embedchain/tree/main/configs).
Now, you can upload this config file in the request body.
For example,
```bash Request
curl --request POST \
--url http://localhost:8080/create?app_id=my-app \
-F "config=@/path/to/config.yaml"
```
**Note:** To use custom models, an **API key** might be required. Refer to the table below to determine the necessary API key for your provider.
| Keys | Providers |
| -------------------------- | ------------------------------ |
| `OPENAI_API_KEY ` | OpenAI, Azure OpenAI, Jina etc |
| `OPENAI_API_TYPE` | Azure OpenAI |
| `OPENAI_API_BASE` | Azure OpenAI |
| `OPENAI_API_VERSION` | Azure OpenAI |
| `COHERE_API_KEY` | Cohere |
| `ANTHROPIC_API_KEY` | Anthropic |
| `JINACHAT_API_KEY` | Jina |
| `HUGGINGFACE_ACCESS_TOKEN` | Huggingface |
| `REPLICATE_API_TOKEN` | LLAMA2 |
To add env variables, you can simply run the docker command with the `-e` flag.
For example,
```bash
docker run --name embedchain -p 8080:8080 -e OPENAI_API_KEY=<YOUR_OPENAI_API_KEY> embedchain/rest-api:latest
```
+21
View File
@@ -0,0 +1,21 @@
---
openapi: delete /{app_id}/delete
---
<RequestExample>
```bash Request
curl --request DELETE \
--url http://localhost:8080/{app_id}/delete
```
</RequestExample>
<ResponseExample>
```json Response
{ "response": "App with id {app_id} deleted successfully." }
```
</ResponseExample>
+22
View File
@@ -0,0 +1,22 @@
---
openapi: post /{app_id}/deploy
---
<RequestExample>
```bash Request
curl --request POST \
--url http://localhost:8080/{app_id}/deploy \
-d "api_key=ec-xxxx"
```
</RequestExample>
<ResponseExample>
```json Response
{ "response": "App deployed successfully." }
```
</ResponseExample>
+33
View File
@@ -0,0 +1,33 @@
---
openapi: get /apps
---
<RequestExample>
```bash Request
curl --request GET \
--url http://localhost:8080/apps
```
</RequestExample>
<ResponseExample>
```json Response
{
"results": [
{
"config": "config1.yaml",
"id": 1,
"app_id": "app1"
},
{
"config": "config2.yaml",
"id": 2,
"app_id": "app2"
}
]
}
```
</ResponseExample>
+28
View File
@@ -0,0 +1,28 @@
---
openapi: get /{app_id}/data
---
<RequestExample>
```bash Request
curl --request GET \
--url http://localhost:8080/{app_id}/data
```
</RequestExample>
<ResponseExample>
```json Response
{
"results": [
{
"data_type": "web_page",
"data_value": "https://www.forbes.com/profile/elon-musk/",
"metadata": "null"
}
]
}
```
</ResponseExample>
+294
View File
@@ -0,0 +1,294 @@
---
title: "🌍 Getting Started"
---
## Quickstart
To use Embedchain as a REST API service, run the following command:
```bash
docker run --name embedchain -p 8080:8080 embedchain/rest-api:latest
```
Navigate to [http://localhost:8080/docs](http://localhost:8080/docs) to interact with the API. There is a full-fledged Swagger docs playground with all the information about the API endpoints.
![Swagger Docs Screenshot](https://github.com/embedchain/embedchain/assets/73601258/299d81e5-a0df-407c-afc2-6fa2c4286844)
## ⚡ Steps to get started
<Steps>
<Step title="⚙️ Create an app">
<Tabs>
<Tab title="cURL">
```bash
curl --request POST "http://localhost:8080/create?app_id=my-app" \
-H "accept: application/json"
```
</Tab>
<Tab title="python">
```python
import requests
url = "http://localhost:8080/create?app_id=my-app"
payload={}
response = requests.request("POST", url, data=payload)
print(response)
```
</Tab>
<Tab title="javascript">
```javascript
const data = fetch("http://localhost:8080/create?app_id=my-app", {
method: "POST",
}).then((res) => res.json());
console.log(data);
```
</Tab>
<Tab title="go">
```go
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "http://localhost:8080/create?app_id=my-app"
payload := strings.NewReader("")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
```
</Tab>
</Tabs>
</Step>
<Step title="🗃️ Add data sources">
<Tabs>
<Tab title="cURL">
```bash
curl --request POST \
--url http://localhost:8080/my-app/add \
-d "source=https://www.forbes.com/profile/elon-musk" \
-d "data_type=web_page"
```
</Tab>
<Tab title="python">
```python
import requests
url = "http://localhost:8080/my-app/add"
payload = "source=https://www.forbes.com/profile/elon-musk&data_type=web_page"
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response)
```
</Tab>
<Tab title="javascript">
```javascript
const data = fetch("http://localhost:8080/my-app/add", {
method: "POST",
body: "source=https://www.forbes.com/profile/elon-musk&data_type=web_page",
}).then((res) => res.json());
console.log(data);
```
</Tab>
<Tab title="go">
```go
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "http://localhost:8080/my-app/add"
payload := strings.NewReader("source=https://www.forbes.com/profile/elon-musk&data_type=web_page")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
```
</Tab>
</Tabs>
</Step>
<Step title="💬 Query on your data">
<Tabs>
<Tab title="cURL">
```bash
curl --request POST \
--url http://localhost:8080/my-app/query \
-d "query=Who is Elon Musk?"
```
</Tab>
<Tab title="python">
```python
import requests
url = "http://localhost:8080/my-app/query"
payload = "query=Who is Elon Musk?"
headers = {}
response = requests.request("POST", url, headers=headers, data=payload)
print(response)
```
</Tab>
<Tab title="javascript">
```javascript
const data = fetch("http://localhost:8080/my-app/query", {
method: "POST",
body: "query=Who is Elon Musk?",
}).then((res) => res.json());
console.log(data);
```
</Tab>
<Tab title="go">
```go
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "http://localhost:8080/my-app/query"
payload := strings.NewReader("query=Who is Elon Musk?")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
```
</Tab>
</Tabs>
</Step>
<Step title="🚀 (Optional) Deploy your app to Embedchain Platform">
<Tabs>
<Tab title="cURL">
```bash
curl --request POST \
--url http://localhost:8080/my-app/deploy \
-d "api_key=ec-xxxx"
```
</Tab>
<Tab title="python">
```python
import requests
url = "http://localhost:8080/my-app/deploy"
payload = "api_key=ec-xxxx"
response = requests.request("POST", url, data=payload)
print(response)
```
</Tab>
<Tab title="javascript">
```javascript
const data = fetch("http://localhost:8080/my-app/deploy", {
method: "POST",
body: "api_key=ec-xxxx",
}).then((res) => res.json());
console.log(data);
```
</Tab>
<Tab title="go">
```go
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "http://localhost:8080/my-app/deploy"
payload := strings.NewReader("api_key=ec-xxxx")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
```
</Tab>
</Tabs>
</Step>
</Steps>
And you're ready! 🎉
If you run into issues, please feel free to contact us using below links:
<Snippet file="get-help.mdx" />
+21
View File
@@ -0,0 +1,21 @@
---
openapi: post /{app_id}/query
---
<RequestExample>
```bash Request
curl --request POST \
--url http://localhost:8080/{app_id}/query \
-d "query=who is Elon Musk?"
```
</RequestExample>
<ResponseExample>
```json Response
{ "response": "Net worth of Elon Musk is $218 Billion." }
```
</ResponseExample>
+21 -5
View File
@@ -2,7 +2,9 @@ from typing import Optional
import yaml
from embedchain.config import AppConfig, BaseEmbedderConfig, BaseLlmConfig
from embedchain.client import Client
from embedchain.config import (AppConfig, BaseEmbedderConfig, BaseLlmConfig,
ChunkerConfig)
from embedchain.config.vectordb.base import BaseVectorDbConfig
from embedchain.embedchain import EmbedChain
from embedchain.embedder.base import BaseEmbedder
@@ -11,6 +13,7 @@ from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory
from embedchain.helper.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm
from embedchain.llm.openai import OpenAILlm
from embedchain.utils import validate_yaml_config
from embedchain.vectordb.base import BaseVectorDB
from embedchain.vectordb.chroma import ChromaDB
@@ -37,6 +40,7 @@ class App(EmbedChain):
embedder: BaseEmbedder = None,
embedder_config: Optional[BaseEmbedderConfig] = None,
system_prompt: Optional[str] = None,
chunker: Optional[ChunkerConfig] = None,
):
"""
Initialize a new `App` instance.
@@ -64,6 +68,9 @@ class App(EmbedChain):
:type system_prompt: Optional[str], optional
: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
if config and not isinstance(config, AppConfig):
raise TypeError(
@@ -96,6 +103,9 @@ class App(EmbedChain):
if embedder is None:
embedder = OpenAIEmbedder(config=embedder_config)
self.chunker = None
if chunker:
self.chunker = ChunkerConfig(**chunker)
# Type check assignments
if not isinstance(llm, BaseLlm):
raise TypeError(
@@ -127,10 +137,16 @@ class App(EmbedChain):
with open(yaml_path, "r") as file:
config_data = yaml.safe_load(file)
try:
validate_yaml_config(config_data)
except Exception as e:
raise Exception(f"❌ Error occurred while validating the YAML config. Error: {str(e)}")
app_config_data = config_data.get("app", {})
llm_config_data = config_data.get("llm", {})
db_config_data = config_data.get("vectordb", {})
embedder_config_data = config_data.get("embedder", {})
embedding_model_config_data = config_data.get("embedding_model", config_data.get("embedder", {}))
chunker_config_data = config_data.get("chunker", {})
app_config = AppConfig(**app_config_data.get("config", {}))
@@ -140,6 +156,6 @@ class App(EmbedChain):
db_provider = db_config_data.get("provider", "chroma")
db = VectorDBFactory.create(db_provider, db_config_data.get("config", {}))
embedder_provider = embedder_config_data.get("provider", "openai")
embedder = EmbedderFactory.create(embedder_provider, embedder_config_data.get("config", {}))
return cls(config=app_config, llm=llm, db=db, embedder=embedder)
embedder_provider = embedding_model_config_data.get("provider", "openai")
embedder = EmbedderFactory.create(embedder_provider, embedding_model_config_data.get("config", {}))
return cls(config=app_config, llm=llm, db=db, embedder=embedder, chunker=chunker_config_data)
+3 -3
View File
@@ -1,7 +1,7 @@
from typing import Any
from embedchain import App
from embedchain.config import AddConfig, AppConfig, BaseLlmConfig
from embedchain import Pipeline as App
from embedchain.config import AddConfig, BaseLlmConfig, PipelineConfig
from embedchain.embedder.openai import OpenAIEmbedder
from embedchain.helper.json_serializable import (JSONSerializable,
register_deserializable)
@@ -12,7 +12,7 @@ from embedchain.vectordb.chroma import ChromaDB
@register_deserializable
class BaseBot(JSONSerializable):
def __init__(self):
self.app = App(config=AppConfig(), llm=OpenAILlm(), db=ChromaDB(), embedder=OpenAIEmbedder())
self.app = App(config=PipelineConfig(), llm=OpenAILlm(), db=ChromaDB(), embedding_model=OpenAIEmbedder())
def add(self, data: Any, config: AddConfig = None):
"""
+22
View File
@@ -0,0 +1,22 @@
from typing import Optional
from langchain.text_splitter import RecursiveCharacterTextSplitter
from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config.add_config import ChunkerConfig
from embedchain.helper.json_serializable import register_deserializable
@register_deserializable
class PostgresChunker(BaseChunker):
"""Chunker for postgres."""
def __init__(self, config: Optional[ChunkerConfig] = None):
if config is None:
config = ChunkerConfig(chunk_size=1000, chunk_overlap=0, length_function=len)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=config.chunk_size,
chunk_overlap=config.chunk_overlap,
length_function=config.length_function,
)
super().__init__(text_splitter)
+1 -1
View File
@@ -5,7 +5,7 @@ import uuid
import requests
from embedchain.embedchain import CONFIG_DIR, CONFIG_FILE
from embedchain.constants import CONFIG_DIR, CONFIG_FILE
class Client:
+14 -1
View File
@@ -1,3 +1,5 @@
import builtins
from importlib import import_module
from typing import Callable, Optional
from embedchain.config.base_config import BaseConfig
@@ -18,7 +20,18 @@ class ChunkerConfig(BaseConfig):
):
self.chunk_size = chunk_size if chunk_size else 2000
self.chunk_overlap = chunk_overlap if chunk_overlap else 0
self.length_function = length_function if length_function else len
if isinstance(length_function, str):
self.length_function = self.load_func(length_function)
else:
self.length_function = length_function if length_function else len
def load_func(self, dotpath: str):
if "." not in dotpath:
return getattr(builtins, dotpath)
else:
module_, func = dotpath.rsplit(".", maxsplit=1)
m = import_module(module_)
return getattr(m, func)
@register_deserializable
+1 -1
View File
@@ -15,7 +15,7 @@ class AppConfig(BaseAppConfig):
self,
log_level: str = "WARNING",
id: Optional[str] = None,
collect_metrics: Optional[bool] = None,
collect_metrics: Optional[bool] = True,
collection_name: Optional[str] = None,
):
"""
+1 -1
View File
@@ -16,7 +16,7 @@ class PipelineConfig(BaseAppConfig):
log_level: str = "WARNING",
id: Optional[str] = None,
name: Optional[str] = None,
collect_metrics: Optional[bool] = False,
collect_metrics: Optional[bool] = True,
):
"""
Initializes a configuration class instance for an App. This is the simplest form of an embedchain app.
+8
View File
@@ -0,0 +1,8 @@
import os
from pathlib import Path
ABS_PATH = os.getcwd()
HOME_DIR = str(Path.home())
CONFIG_DIR = os.path.join(HOME_DIR, ".embedchain")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
SQLITE_PATH = os.path.join(CONFIG_DIR, "embedchain.db")
+79 -96
View File
@@ -1,41 +1,11 @@
from importlib import import_module
from typing import Any, Dict
from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.chunkers.docs_site import DocsSiteChunker
from embedchain.chunkers.docx_file import DocxFileChunker
from embedchain.chunkers.gmail import GmailChunker
from embedchain.chunkers.images import ImagesChunker
from embedchain.chunkers.json import JSONChunker
from embedchain.chunkers.mdx import MdxChunker
from embedchain.chunkers.notion import NotionChunker
from embedchain.chunkers.openapi import OpenAPIChunker
from embedchain.chunkers.pdf_file import PdfFileChunker
from embedchain.chunkers.qna_pair import QnaPairChunker
from embedchain.chunkers.sitemap import SitemapChunker
from embedchain.chunkers.table import TableChunker
from embedchain.chunkers.text import TextChunker
from embedchain.chunkers.unstructured_file import UnstructuredFileChunker
from embedchain.chunkers.web_page import WebPageChunker
from embedchain.chunkers.xml import XmlChunker
from embedchain.chunkers.youtube_video import YoutubeVideoChunker
from embedchain.config import AddConfig
from embedchain.config.add_config import ChunkerConfig, LoaderConfig
from embedchain.helper.json_serializable import JSONSerializable
from embedchain.loaders.base_loader import BaseLoader
from embedchain.loaders.csv import CsvLoader
from embedchain.loaders.docs_site_loader import DocsSiteLoader
from embedchain.loaders.docx_file import DocxFileLoader
from embedchain.loaders.gmail import GmailLoader
from embedchain.loaders.images import ImagesLoader
from embedchain.loaders.json import JSONLoader
from embedchain.loaders.local_qna_pair import LocalQnaPairLoader
from embedchain.loaders.local_text import LocalTextLoader
from embedchain.loaders.mdx import MdxLoader
from embedchain.loaders.openapi import OpenAPILoader
from embedchain.loaders.pdf_file import PdfFileLoader
from embedchain.loaders.sitemap import SitemapLoader
from embedchain.loaders.unstructured_file import UnstructuredLoader
from embedchain.loaders.web_page import WebPageLoader
from embedchain.loaders.xml import XmlLoader
from embedchain.loaders.youtube_video import YoutubeVideoLoader
from embedchain.models.data_type import DataType
@@ -46,7 +16,7 @@ class DataFormatter(JSONSerializable):
.add or .add_local method call
"""
def __init__(self, data_type: DataType, config: AddConfig):
def __init__(self, data_type: DataType, config: AddConfig, kwargs: Dict[str, Any]):
"""
Initialize a dataformatter, set data type and chunker based on datatype.
@@ -55,10 +25,15 @@ class DataFormatter(JSONSerializable):
:param config: AddConfig instance with nested loader and chunker config attributes.
:type config: AddConfig
"""
self.loader = self._get_loader(data_type=data_type, config=config.loader)
self.chunker = self._get_chunker(data_type=data_type, config=config.chunker)
self.loader = self._get_loader(data_type=data_type, config=config.loader, kwargs=kwargs)
self.chunker = self._get_chunker(data_type=data_type, config=config.chunker, kwargs=kwargs)
def _get_loader(self, data_type: DataType, config: LoaderConfig) -> BaseLoader:
def _lazy_load(self, module_path: str):
module_path, class_name = module_path.rsplit(".", 1)
module = import_module(module_path)
return getattr(module, class_name)
def _get_loader(self, data_type: DataType, config: LoaderConfig, kwargs: Dict[str, Any]) -> BaseLoader:
"""
Returns the appropriate data loader for the given data type.
@@ -71,72 +46,80 @@ class DataFormatter(JSONSerializable):
:rtype: BaseLoader
"""
loaders = {
DataType.YOUTUBE_VIDEO: YoutubeVideoLoader,
DataType.PDF_FILE: PdfFileLoader,
DataType.WEB_PAGE: WebPageLoader,
DataType.QNA_PAIR: LocalQnaPairLoader,
DataType.TEXT: LocalTextLoader,
DataType.DOCX: DocxFileLoader,
DataType.SITEMAP: SitemapLoader,
DataType.XML: XmlLoader,
DataType.DOCS_SITE: DocsSiteLoader,
DataType.CSV: CsvLoader,
DataType.MDX: MdxLoader,
DataType.IMAGES: ImagesLoader,
DataType.UNSTRUCTURED: UnstructuredLoader,
DataType.JSON: JSONLoader,
DataType.OPENAPI: OpenAPILoader,
DataType.GMAIL: GmailLoader,
DataType.YOUTUBE_VIDEO: "embedchain.loaders.youtube_video.YoutubeVideoLoader",
DataType.PDF_FILE: "embedchain.loaders.pdf_file.PdfFileLoader",
DataType.WEB_PAGE: "embedchain.loaders.web_page.WebPageLoader",
DataType.QNA_PAIR: "embedchain.loaders.local_qna_pair.LocalQnaPairLoader",
DataType.TEXT: "embedchain.loaders.local_text.LocalTextLoader",
DataType.DOCX: "embedchain.loaders.docx_file.DocxFileLoader",
DataType.SITEMAP: "embedchain.loaders.sitemap.SitemapLoader",
DataType.XML: "embedchain.loaders.xml.XmlLoader",
DataType.DOCS_SITE: "embedchain.loaders.docs_site_loader.DocsSiteLoader",
DataType.CSV: "embedchain.loaders.csv.CsvLoader",
DataType.MDX: "embedchain.loaders.mdx.MdxLoader",
DataType.IMAGES: "embedchain.loaders.images.ImagesLoader",
DataType.UNSTRUCTURED: "embedchain.loaders.unstructured_file.UnstructuredLoader",
DataType.JSON: "embedchain.loaders.json.JSONLoader",
DataType.OPENAPI: "embedchain.loaders.openapi.OpenAPILoader",
DataType.GMAIL: "embedchain.loaders.gmail.GmailLoader",
DataType.NOTION: "embedchain.loaders.notion.NotionLoader",
}
lazy_loaders = {DataType.NOTION}
custom_loaders = set(
[
DataType.POSTGRES,
]
)
if data_type in loaders:
loader_class: type = loaders[data_type]
loader: BaseLoader = loader_class()
return loader
elif data_type in lazy_loaders:
if data_type == DataType.NOTION:
from embedchain.loaders.notion import NotionLoader
loader_class: type = self._lazy_load(loaders[data_type])
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
return NotionLoader()
else:
raise ValueError(f"Unsupported data type: {data_type}")
else:
raise ValueError(f"Unsupported data type: {data_type}")
raise ValueError(
f"Cant find the loader for {data_type}.\
We recommend to pass the loader to use data_type: {data_type},\
check `https://docs.embedchain.ai/data-sources/overview`."
)
def _get_chunker(self, data_type: DataType, config: ChunkerConfig) -> BaseChunker:
"""Returns the appropriate chunker for the given data type.
:param data_type: The type of the data to chunk.
:type data_type: DataType
:param config: Config to initialize the chunker with.
:type config: ChunkerConfig
:raises ValueError: If an unsupported data type is provided.
:return: The chunker for the given data type.
:rtype: BaseChunker
"""
def _get_chunker(self, data_type: DataType, config: ChunkerConfig, kwargs: Dict[str, Any]) -> BaseChunker:
"""Returns the appropriate chunker for the given data type (updated for lazy loading)."""
chunker_classes = {
DataType.YOUTUBE_VIDEO: YoutubeVideoChunker,
DataType.PDF_FILE: PdfFileChunker,
DataType.WEB_PAGE: WebPageChunker,
DataType.QNA_PAIR: QnaPairChunker,
DataType.TEXT: TextChunker,
DataType.DOCX: DocxFileChunker,
DataType.DOCS_SITE: DocsSiteChunker,
DataType.SITEMAP: SitemapChunker,
DataType.NOTION: NotionChunker,
DataType.CSV: TableChunker,
DataType.MDX: MdxChunker,
DataType.IMAGES: ImagesChunker,
DataType.XML: XmlChunker,
DataType.UNSTRUCTURED: UnstructuredFileChunker,
DataType.JSON: JSONChunker,
DataType.OPENAPI: OpenAPIChunker,
DataType.GMAIL: GmailChunker,
DataType.YOUTUBE_VIDEO: "embedchain.chunkers.youtube_video.YoutubeVideoChunker",
DataType.PDF_FILE: "embedchain.chunkers.pdf_file.PdfFileChunker",
DataType.WEB_PAGE: "embedchain.chunkers.web_page.WebPageChunker",
DataType.QNA_PAIR: "embedchain.chunkers.qna_pair.QnaPairChunker",
DataType.TEXT: "embedchain.chunkers.text.TextChunker",
DataType.DOCX: "embedchain.chunkers.docx_file.DocxFileChunker",
DataType.SITEMAP: "embedchain.chunkers.sitemap.SitemapChunker",
DataType.XML: "embedchain.chunkers.xml.XmlChunker",
DataType.DOCS_SITE: "embedchain.chunkers.docs_site.DocsSiteChunker",
DataType.CSV: "embedchain.chunkers.table.TableChunker",
DataType.MDX: "embedchain.chunkers.mdx.MdxChunker",
DataType.IMAGES: "embedchain.chunkers.images.ImagesChunker",
DataType.UNSTRUCTURED: "embedchain.chunkers.unstructured_file.UnstructuredFileChunker",
DataType.JSON: "embedchain.chunkers.json.JSONChunker",
DataType.OPENAPI: "embedchain.chunkers.openapi.OpenAPIChunker",
DataType.GMAIL: "embedchain.chunkers.gmail.GmailChunker",
DataType.NOTION: "embedchain.chunkers.notion.NotionChunker",
DataType.POSTGRES: "embedchain.chunkers.postgres.PostgresChunker",
}
if data_type in chunker_classes:
chunker_class: type = chunker_classes[data_type]
chunker: BaseChunker = chunker_class(config)
if "chunker" in kwargs:
chunker_class = kwargs.get("chunker")
else:
chunker_class = self._lazy_load(chunker_classes[data_type])
chunker = chunker_class(config)
chunker.set_data_type(data_type)
return chunker
else:
raise ValueError(f"Unsupported data type: {data_type}")
raise ValueError(
f"Cant find the chunker for {data_type}.\
We recommend to pass the chunker to use data_type: {data_type},\
check `https://docs.embedchain.ai/data-sources/overview`."
)
+123 -105
View File
@@ -1,22 +1,16 @@
import hashlib
import importlib.metadata
import json
import logging
import os
import sqlite3
import threading
import uuid
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Tuple, Union
import requests
from dotenv import load_dotenv
from langchain.docstore.document import Document
from tenacity import retry, stop_after_attempt, wait_fixed
from embedchain.chunkers.base_chunker import BaseChunker
from embedchain.config import AddConfig, BaseLlmConfig
from embedchain.config import AddConfig, BaseLlmConfig, ChunkerConfig
from embedchain.config.apps.base_app_config import BaseAppConfig
from embedchain.constants import SQLITE_PATH
from embedchain.data_formatter import DataFormatter
from embedchain.embedder.base import BaseEmbedder
from embedchain.helper.json_serializable import JSONSerializable
@@ -24,17 +18,12 @@ from embedchain.llm.base import BaseLlm
from embedchain.loaders.base_loader import BaseLoader
from embedchain.models.data_type import (DataType, DirectDataType,
IndirectDataType, SpecialDataType)
from embedchain.utils import detect_datatype
from embedchain.telemetry.posthog import AnonymousTelemetry
from embedchain.utils import detect_datatype, is_valid_json_string
from embedchain.vectordb.base import BaseVectorDB
load_dotenv()
ABS_PATH = os.getcwd()
HOME_DIR = str(Path.home())
CONFIG_DIR = os.path.join(HOME_DIR, ".embedchain")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
SQLITE_PATH = os.path.join(CONFIG_DIR, "embedchain.db")
class EmbedChain(JSONSerializable):
def __init__(
@@ -85,15 +74,18 @@ class EmbedChain(JSONSerializable):
if system_prompt:
self.llm.config.system_prompt = system_prompt
# Fetch the history from the database if exists
self.llm.update_history(app_id=self.config.id)
# Attributes that aren't subclass related.
self.user_asks = []
self.chunker: ChunkerConfig = None
# Send anonymous telemetry
self.s_id = self.config.id if self.config.id else str(uuid.uuid4())
self.u_id = self._load_or_generate_user_id()
self._telemetry_props = {"class": self.__class__.__name__}
self.telemetry = AnonymousTelemetry(enabled=self.config.collect_metrics)
# Establish a connection to the SQLite database
self.connection = sqlite3.connect(SQLITE_PATH)
self.connection = sqlite3.connect(SQLITE_PATH, check_same_thread=False)
self.cursor = self.connection.cursor()
# Create the 'data_sources' table if it doesn't exist
@@ -111,12 +103,8 @@ class EmbedChain(JSONSerializable):
"""
)
self.connection.commit()
# NOTE: Uncomment the next two lines when running tests to see if any test fires a telemetry event.
# if (self.config.collect_metrics):
# raise ConnectionRefusedError("Collection of metrics should not be allowed.")
thread_telemetry = threading.Thread(target=self._send_telemetry_event, args=("init",))
thread_telemetry.start()
# Send anonymous telemetry
self.telemetry.capture(event_name="init", properties=self._telemetry_props)
@property
def collect_metrics(self):
@@ -138,29 +126,6 @@ class EmbedChain(JSONSerializable):
raise ValueError(f"Boolean value expected but got {type(value)}.")
self.llm.online = value
def _load_or_generate_user_id(self) -> str:
"""
Loads the user id from the config file if it exists, otherwise generates a new
one and saves it to the config file.
:return: user id
:rtype: str
"""
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r") as f:
data = json.load(f)
if "user_id" in data:
return data["user_id"]
u_id = str(uuid.uuid4())
with open(CONFIG_FILE, "w") as f:
json.dump({"user_id": u_id}, f)
return u_id
def add(
self,
source: Any,
@@ -168,6 +133,7 @@ class EmbedChain(JSONSerializable):
metadata: Optional[Dict[str, Any]] = None,
config: Optional[AddConfig] = None,
dry_run=False,
**kwargs: Dict[str, Any],
):
"""
Adds the data from the given URL to the vector db.
@@ -189,7 +155,11 @@ class EmbedChain(JSONSerializable):
:return: source_hash, a md5-hash of the source, in hexadecimal representation.
:rtype: str
"""
if config is None:
if config is not None:
pass
elif self.chunker is not None:
config = AddConfig(chunker=self.chunker)
else:
config = AddConfig()
try:
@@ -212,6 +182,7 @@ class EmbedChain(JSONSerializable):
f"Invalid data_type: '{data_type}'.",
f"Please use one of the following: {[data_type.value for data_type in DataType]}",
) from None
if not data_type:
data_type = detect_datatype(source)
@@ -229,8 +200,9 @@ class EmbedChain(JSONSerializable):
print(f"Data with hash {source_hash} already exists. Skipping addition.")
return source_hash
data_formatter = DataFormatter(data_type, config)
self.user_asks.append([source, data_type.value, metadata])
data_formatter = DataFormatter(data_type, config, kwargs)
documents, metadatas, _ids, new_chunks = self.load_and_embed(
data_formatter.loader, data_formatter.chunker, source, metadata, source_hash, dry_run
)
@@ -259,9 +231,14 @@ class EmbedChain(JSONSerializable):
# it's quicker to check the variable twice than to count words when they won't be submitted.
word_count = data_formatter.chunker.get_word_count(documents)
extra_metadata = {"data_type": data_type.value, "word_count": word_count, "chunks_count": new_chunks}
thread_telemetry = threading.Thread(target=self._send_telemetry_event, args=("add", extra_metadata))
thread_telemetry.start()
# Send anonymous telemetry
event_properties = {
**self._telemetry_props,
"data_type": data_type.value,
"word_count": word_count,
"chunks_count": new_chunks,
}
self.telemetry.capture(event_name="add", properties=event_properties)
return source_hash
@@ -271,6 +248,7 @@ class EmbedChain(JSONSerializable):
data_type: Optional[DataType] = None,
metadata: Optional[Dict[str, Any]] = None,
config: Optional[AddConfig] = None,
**kwargs: Dict[str, Any],
):
"""
Adds the data from the given URL to the vector db.
@@ -296,7 +274,13 @@ class EmbedChain(JSONSerializable):
logging.warning(
"The `add_local` method is deprecated and will be removed in future versions. Please use the `add` method for both local and remote files." # noqa: E501
)
return self.add(source=source, data_type=data_type, metadata=metadata, config=config)
return self.add(
source=source,
data_type=data_type,
metadata=metadata,
config=config,
kwargs=kwargs,
)
def _get_existing_doc_id(self, chunker: BaseChunker, src: Any):
"""
@@ -314,6 +298,10 @@ class EmbedChain(JSONSerializable):
# These types have a indirect source reference
# As long as the reference is the same, they can be updated.
where = {"url": src}
if chunker.data_type == DataType.JSON and is_valid_json_string(src):
url = hashlib.sha256((src).encode("utf-8")).hexdigest()
where = {"url": url}
if self.config.id is not None:
where.update({"app_id": self.config.id})
@@ -395,6 +383,10 @@ class EmbedChain(JSONSerializable):
# get existing ids, and discard doc if any common id exist.
where = {"url": src}
if chunker.data_type == DataType.JSON and is_valid_json_string(src):
url = hashlib.sha256((src).encode("utf-8")).hexdigest()
where = {"url": url}
# if data type is qna_pair, we check for question
if chunker.data_type == DataType.QNA_PAIR:
where = {"question": src[0]}
@@ -465,7 +457,9 @@ class EmbedChain(JSONSerializable):
)
]
def retrieve_from_database(self, input_query: str, config: Optional[BaseLlmConfig] = None, where=None) -> List[str]:
def retrieve_from_database(
self, input_query: str, config: Optional[BaseLlmConfig] = None, where=None, citations: bool = False
) -> Union[List[Tuple[str, str, str]], List[str]]:
"""
Queries the vector database based on the given input query.
Gets relevant doc based on the query
@@ -476,19 +470,21 @@ class EmbedChain(JSONSerializable):
:type config: Optional[BaseLlmConfig], optional
:param where: A dictionary of key-value pairs to filter the database results, defaults to None
:type where: _type_, optional
:param citations: A boolean to indicate if db should fetch citation source
:type citations: bool
:return: List of contents of the document that matched your query
:rtype: List[str]
"""
query_config = config or self.llm.config
if where is not None:
where = where
elif query_config is not None and query_config.where is not None:
where = query_config.where
else:
where = {}
if self.config.id is not None:
where.update({"app_id": self.config.id})
if query_config is not None and query_config.where is not None:
where = query_config.where
if self.config.id is not None:
where.update({"app_id": self.config.id})
# We cannot query the database with the input query in case of an image search. This is because we need
# to bring down both the image and text to the same dimension to be able to compare them.
@@ -505,14 +501,19 @@ class EmbedChain(JSONSerializable):
n_results=query_config.number_documents,
where=where,
skip_embedding=(hasattr(config, "query_type") and config.query_type == "Images"),
citations=citations,
)
if len(contexts) > 0 and isinstance(contexts[0], tuple):
contexts = list(map(lambda x: x[0], contexts))
return contexts
def query(self, input_query: str, config: BaseLlmConfig = None, dry_run=False, where: Optional[Dict] = None) -> str:
def query(
self,
input_query: str,
config: BaseLlmConfig = None,
dry_run=False,
where: Optional[Dict] = None,
**kwargs: Dict[str, Any],
) -> Union[Tuple[str, List[Tuple[str, str, str]]], str]:
"""
Queries the vector database based on the given input query.
Gets relevant doc based on the query and then passes it to an
@@ -528,17 +529,31 @@ class EmbedChain(JSONSerializable):
:type dry_run: bool, optional
:param where: A dictionary of key-value pairs to filter the database results., defaults to None
:type where: Optional[Dict[str, str]], optional
:return: The answer to the query or the dry run result
:rtype: str
:param kwargs: To read more params for the query function. Ex. we use citations boolean
param to return context along with the answer
:type kwargs: Dict[str, Any]
:return: The answer to the query, with citations if the citation flag is True
or the dry run result
:rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]]
"""
contexts = self.retrieve_from_database(input_query=input_query, config=config, where=where)
answer = self.llm.query(input_query=input_query, contexts=contexts, config=config, dry_run=dry_run)
citations = kwargs.get("citations", False)
contexts = self.retrieve_from_database(input_query=input_query, config=config, where=where, citations=citations)
if citations and len(contexts) > 0 and isinstance(contexts[0], tuple):
contexts_data_for_llm_query = list(map(lambda x: x[0], contexts))
else:
contexts_data_for_llm_query = contexts
answer = self.llm.query(
input_query=input_query, contexts=contexts_data_for_llm_query, config=config, dry_run=dry_run
)
# Send anonymous telemetry
thread_telemetry = threading.Thread(target=self._send_telemetry_event, args=("query",))
thread_telemetry.start()
self.telemetry.capture(event_name="query", properties=self._telemetry_props)
return answer
if citations:
return answer, contexts
else:
return answer
def chat(
self,
@@ -546,6 +561,7 @@ class EmbedChain(JSONSerializable):
config: Optional[BaseLlmConfig] = None,
dry_run=False,
where: Optional[Dict[str, str]] = None,
**kwargs: Dict[str, Any],
) -> str:
"""
Queries the vector database on the given input query.
@@ -564,17 +580,34 @@ class EmbedChain(JSONSerializable):
:type dry_run: bool, optional
:param where: A dictionary of key-value pairs to filter the database results., defaults to None
:type where: Optional[Dict[str, str]], optional
:return: The answer to the query or the dry run result
:rtype: str
:param kwargs: To read more params for the query function. Ex. we use citations boolean
param to return context along with the answer
:type kwargs: Dict[str, Any]
:return: The answer to the query, with citations if the citation flag is True
or the dry run result
:rtype: str, if citations is False, otherwise Tuple[str,List[Tuple[str,str,str]]]
"""
contexts = self.retrieve_from_database(input_query=input_query, config=config, where=where)
answer = self.llm.chat(input_query=input_query, contexts=contexts, config=config, dry_run=dry_run)
citations = kwargs.get("citations", False)
contexts = self.retrieve_from_database(input_query=input_query, config=config, where=where, citations=citations)
if citations and len(contexts) > 0 and isinstance(contexts[0], tuple):
contexts_data_for_llm_query = list(map(lambda x: x[0], contexts))
else:
contexts_data_for_llm_query = contexts
answer = self.llm.chat(
input_query=input_query, contexts=contexts_data_for_llm_query, config=config, dry_run=dry_run
)
# add conversation in memory
self.llm.add_history(self.config.id, input_query, answer)
# Send anonymous telemetry
thread_telemetry = threading.Thread(target=self._send_telemetry_event, args=("chat",))
thread_telemetry.start()
self.telemetry.capture(event_name="chat", properties=self._telemetry_props)
return answer
if citations:
return answer, contexts
else:
return answer
def set_collection_name(self, name: str):
"""
@@ -608,34 +641,19 @@ class EmbedChain(JSONSerializable):
Resets the database. Deletes all embeddings irreversibly.
`App` does not have to be reinitialized after using this method.
"""
# Send anonymous telemetry
thread_telemetry = threading.Thread(target=self._send_telemetry_event, args=("reset",))
thread_telemetry.start()
self.db.reset()
self.cursor.execute("DELETE FROM data_sources WHERE pipeline_id = ?", (self.config.id,))
self.connection.commit()
self.delete_history()
# Send anonymous telemetry
self.telemetry.capture(event_name="reset", properties=self._telemetry_props)
@retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
def _send_telemetry_event(self, method: str, extra_metadata: Optional[dict] = None):
"""
Send telemetry event to the embedchain server. This is anonymous. It can be toggled off in `AppConfig`.
"""
if not self.config.collect_metrics:
return
def get_history(self, num_rounds: int = 10, display_format: bool = True):
return self.llm.memory.get_recent_memories(
app_id=self.config.id,
num_rounds=num_rounds,
display_format=display_format,
)
with threading.Lock():
url = "https://api.embedchain.ai/api/v1/telemetry/"
metadata = {
"s_id": self.s_id,
"version": importlib.metadata.version(__package__ or __name__),
"method": method,
"language": "py",
"u_id": self.u_id,
}
if extra_metadata:
metadata.update(extra_metadata)
response = requests.post(url, json={"metadata": metadata})
if response.status_code != 200:
logging.warning(f"Telemetry event failed with status code {response.status_code}")
def delete_history(self):
self.llm.memory.delete_chat_history(app_id=self.config.id)
+104
View File
@@ -0,0 +1,104 @@
"""
Note that this file is copied from Chroma repository. We will remove this file once the fix in
ChromaDB's repository.
"""
from typing import Optional
from chromadb.api.types import Documents, Embeddings
class OpenAIEmbeddingFunction:
def __init__(
self,
api_key: Optional[str] = None,
model_name: str = "text-embedding-ada-002",
organization_id: Optional[str] = None,
api_base: Optional[str] = None,
api_type: Optional[str] = None,
api_version: Optional[str] = None,
deployment_id: Optional[str] = None,
):
"""
Initialize the OpenAIEmbeddingFunction.
Args:
api_key (str, optional): Your API key for the OpenAI API. If not
provided, it will raise an error to provide an OpenAI API key.
organization_id(str, optional): The OpenAI organization ID if applicable
model_name (str, optional): The name of the model to use for text
embeddings. Defaults to "text-embedding-ada-002".
api_base (str, optional): The base path for the API. If not provided,
it will use the base path for the OpenAI API. This can be used to
point to a different deployment, such as an Azure deployment.
api_type (str, optional): The type of the API deployment. This can be
used to specify a different deployment, such as 'azure'. If not
provided, it will use the default OpenAI deployment.
api_version (str, optional): The api version for the API. If not provided,
it will use the api version for the OpenAI API. This can be used to
point to a different deployment, such as an Azure deployment.
deployment_id (str, optional): Deployment ID for Azure OpenAI.
"""
try:
import openai
except ImportError:
raise ValueError("The openai python package is not installed. Please install it with `pip install openai`")
if api_key is not None:
openai.api_key = api_key
# If the api key is still not set, raise an error
elif openai.api_key is None:
raise ValueError(
"Please provide an OpenAI API key. You can get one at https://platform.openai.com/account/api-keys"
)
if api_base is not None:
openai.api_base = api_base
if api_version is not None:
openai.api_version = api_version
self._api_type = api_type
if api_type is not None:
openai.api_type = api_type
if organization_id is not None:
openai.organization = organization_id
self._v1 = openai.__version__.startswith("1.")
if self._v1:
if api_type == "azure":
self._client = openai.AzureOpenAI(
api_key=api_key, api_version=api_version, azure_endpoint=api_base
).embeddings
else:
self._client = openai.OpenAI(api_key=api_key, base_url=api_base).embeddings
else:
self._client = openai.Embedding
self._model_name = model_name
self._deployment_id = deployment_id
def __call__(self, input: Documents) -> Embeddings:
# replace newlines, which can negatively affect performance.
input = [t.replace("\n", " ") for t in input]
# Call the OpenAI Embedding API
if self._v1:
embeddings = self._client.create(input=input, model=self._deployment_id or self._model_name).data
# Sort resulting embeddings by index
sorted_embeddings = sorted(embeddings, key=lambda e: e.index) # type: ignore
# Return just the embeddings
return [result.embedding for result in sorted_embeddings]
else:
if self._api_type == "azure":
embeddings = self._client.create(input=input, engine=self._deployment_id or self._model_name)["data"]
else:
embeddings = self._client.create(input=input, model=self._model_name)["data"]
# Sort resulting embeddings by index
sorted_embeddings = sorted(embeddings, key=lambda e: e["index"]) # type: ignore
# Return just the embeddings
return [result["embedding"] for result in sorted_embeddings]
+2 -9
View File
@@ -7,13 +7,7 @@ from embedchain.config import BaseEmbedderConfig
from embedchain.embedder.base import BaseEmbedder
from embedchain.models import VectorDimensions
try:
from chromadb.utils import embedding_functions
except RuntimeError:
from embedchain.utils import use_pysqlite3
use_pysqlite3()
from chromadb.utils import embedding_functions
from .chroma_embeddings import OpenAIEmbeddingFunction
class OpenAIEmbedder(BaseEmbedder):
@@ -30,11 +24,10 @@ class OpenAIEmbedder(BaseEmbedder):
raise ValueError(
"OPENAI_API_KEY or OPENAI_ORGANIZATION environment variables not provided"
) # noqa:E501
embedding_fn = embedding_functions.OpenAIEmbeddingFunction(
embedding_fn = OpenAIEmbeddingFunction(
api_key=os.getenv("OPENAI_API_KEY"),
organization_id=os.getenv("OPENAI_ORGANIZATION"),
model_name=self.config.model,
)
self.set_embedding_fn(embedding_fn=embedding_fn)
self.set_vector_dimension(vector_dimension=VectorDimensions.OPENAI.value)
+15 -17
View File
@@ -1,14 +1,15 @@
import logging
from typing import Any, Dict, Generator, List, Optional
from langchain.memory import ConversationBufferMemory
from langchain.schema import BaseMessage
from langchain.schema import BaseMessage as LCBaseMessage
from embedchain.config import BaseLlmConfig
from embedchain.config.llm.base import (DEFAULT_PROMPT,
DEFAULT_PROMPT_WITH_HISTORY_TEMPLATE,
DOCS_SITE_PROMPT_TEMPLATE)
from embedchain.helper.json_serializable import JSONSerializable
from embedchain.memory.base import ECChatMemory
from embedchain.memory.message import ChatMessage
class BaseLlm(JSONSerializable):
@@ -23,7 +24,7 @@ class BaseLlm(JSONSerializable):
else:
self.config = config
self.memory = ConversationBufferMemory()
self.memory = ECChatMemory()
self.is_docs_site_instance = False
self.online = False
self.history: Any = None
@@ -44,11 +45,18 @@ class BaseLlm(JSONSerializable):
"""
self.history = history
def update_history(self):
def update_history(self, app_id: str):
"""Update class history attribute with history in memory (for chat method)"""
chat_history = self.memory.load_memory_variables({})["history"]
chat_history = self.memory.get_recent_memories(app_id=app_id, num_rounds=10)
if chat_history:
self.set_history(chat_history)
self.set_history([str(history) for history in chat_history])
def add_history(self, app_id: str, question: str, answer: str, metadata: Optional[Dict[str, Any]] = None):
chat_message = ChatMessage()
chat_message.add_user_message(question, metadata=metadata)
chat_message.add_ai_message(answer, metadata=metadata)
self.memory.add(app_id=app_id, chat_message=chat_message)
self.update_history(app_id=app_id)
def generate_prompt(self, input_query: str, contexts: List[str], **kwargs: Dict[str, Any]) -> str:
"""
@@ -165,7 +173,6 @@ class BaseLlm(JSONSerializable):
for chunk in answer:
streamed_answer = streamed_answer + chunk
yield chunk
self.memory.chat_memory.add_ai_message(streamed_answer)
logging.info(f"Answer: {streamed_answer}")
def query(self, input_query: str, contexts: List[str], config: BaseLlmConfig = None, dry_run=False):
@@ -257,8 +264,6 @@ class BaseLlm(JSONSerializable):
if self.online:
k["web_search_result"] = self.access_search_and_get_results(input_query)
self.update_history()
prompt = self.generate_prompt(input_query, contexts, **k)
logging.info(f"Prompt: {prompt}")
@@ -267,16 +272,9 @@ class BaseLlm(JSONSerializable):
answer = self.get_answer_from_llm(prompt)
self.memory.chat_memory.add_user_message(input_query)
if isinstance(answer, str):
self.memory.chat_memory.add_ai_message(answer)
logging.info(f"Answer: {answer}")
# NOTE: Adding to history before and after. This could be seen as redundant.
# If we change it, we have to change the tests (no big deal).
self.update_history()
return answer
else:
# this is a streamed response and needs to be handled differently.
@@ -287,7 +285,7 @@ class BaseLlm(JSONSerializable):
self.config: BaseLlmConfig = BaseLlmConfig.deserialize(prev_config)
@staticmethod
def _get_messages(prompt: str, system_prompt: Optional[str] = None) -> List[BaseMessage]:
def _get_messages(prompt: str, system_prompt: Optional[str] = None) -> List[LCBaseMessage]:
"""
Construct a list of langchain messages
+1 -1
View File
@@ -13,7 +13,7 @@ class GPT4ALLLlm(BaseLlm):
def __init__(self, config: Optional[BaseLlmConfig] = None):
super().__init__(config=config)
if self.config.model is None:
self.config.model = "orca-mini-3b.ggmlv3.q4_0.bin"
self.config.model = "orca-mini-3b-gguf2-q4_0.gguf"
self.instance = GPT4ALLLlm._get_instance(self.config.model)
self.instance.streaming = self.config.stream
+61 -9
View File
@@ -1,24 +1,76 @@
import hashlib
import json
import os
import re
from langchain.document_loaders.json_loader import \
JSONLoader as LangchainJSONLoader
import requests
from embedchain.loaders.base_loader import BaseLoader
from embedchain.utils import clean_string, is_valid_json_string
langchain_json_jq_schema = 'to_entries | map("\(.key): \(.value|tostring)") | .[]'
VALID_URL_PATTERN = "^https:\/\/[0-9A-z.]+.[0-9A-z.]+.[a-z]+\/.*\.json$"
class JSONLoader(BaseLoader):
@staticmethod
def _get_llama_hub_loader():
try:
from llama_hub.jsondata.base import \
JSONDataReader as LLHUBJSONLoader
except ImportError as e:
raise Exception(
f"Failed to install required packages: {e}, \
install them using `pip install --upgrade 'embedchain[json]`"
)
return LLHUBJSONLoader()
@staticmethod
def _check_content(content):
if not isinstance(content, str):
raise ValueError(
"Invaid content input. \
If you want to upload (list, dict, etc.), do \
`json.dump(data, indent=0)` and add the stringified JSON. \
Check - `https://docs.embedchain.ai/data-sources/json`"
)
@staticmethod
def load_data(content):
"""Load a json file. Each data point is a key value pair."""
JSONLoader._check_content(content)
loader = JSONLoader._get_llama_hub_loader()
data = []
data_content = []
loader = LangchainJSONLoader(content, text_content=False, jq_schema=langchain_json_jq_schema)
docs = loader.load()
content_url_str = content
# Load json data from various sources.
if os.path.isfile(content):
with open(content, "r", encoding="utf-8") as json_file:
json_data = json.load(json_file)
elif re.match(VALID_URL_PATTERN, content):
response = requests.get(content)
if response.status_code == 200:
json_data = response.json()
else:
raise ValueError(
f"Loading data from the given url: {content} failed. \
Make sure the url is working."
)
elif is_valid_json_string(content):
json_data = content
content_url_str = hashlib.sha256((content).encode("utf-8")).hexdigest()
else:
raise ValueError(f"Invalid content to load json data from: {content}")
docs = loader.load_data(json_data)
for doc in docs:
meta_data = doc.metadata
data.append({"content": doc.page_content, "meta_data": {"url": content, "row": meta_data["seq_num"]}})
data_content.append(doc.page_content)
doc_id = hashlib.sha256((content + ", ".join(data_content)).encode()).hexdigest()
doc_content = clean_string(doc.text)
data.append({"content": doc_content, "meta_data": {"url": content_url_str}})
data_content.append(doc_content)
doc_id = hashlib.sha256((content_url_str + ", ".join(data_content)).encode()).hexdigest()
return {"doc_id": doc_id, "data": data}
+73
View File
@@ -0,0 +1,73 @@
import hashlib
import logging
from typing import Any, Dict, Optional
from embedchain.loaders.base_loader import BaseLoader
class PostgresLoader(BaseLoader):
def __init__(self, config: Optional[Dict[str, Any]] = None):
super().__init__()
if not config:
raise ValueError(f"Must provide the valid config. Received: {config}")
self.connection = None
self.cursor = None
self._setup_loader(config=config)
def _setup_loader(self, config: Dict[str, Any]):
try:
import psycopg
except ImportError as e:
raise ImportError(
"Unable to import required packages. \
Run `pip install --upgrade 'embedchain[postgres]'`"
) from e
config_info = ""
if "url" in config:
config_info = config.get("url")
else:
conn_params = []
for key, value in config.items():
conn_params.append(f"{key}={value}")
config_info = " ".join(conn_params)
logging.info(f"Connecting to postrgres sql: {config_info}")
self.connection = psycopg.connect(conninfo=config_info)
self.cursor = self.connection.cursor()
def _check_query(self, query):
if not isinstance(query, str):
raise ValueError(
f"Invalid postgres query: {query}",
"Provide the valid source to add from postgres, \
make sure you are following `https://docs.embedchain.ai/data-sources/postgres`",
)
def load_data(self, query):
self._check_query(query)
try:
data = []
data_content = []
self.cursor.execute(query)
results = self.cursor.fetchall()
for result in results:
doc_content = str(result)
data.append({"content": doc_content, "meta_data": {"url": f"postgres_query-({query})"}})
data_content.append(doc_content)
doc_id = hashlib.sha256((query + ", ".join(data_content)).encode()).hexdigest()
return {
"doc_id": doc_id,
"data": data,
}
except Exception as e:
raise ValueError(f"Failed to load data using query={query} with: {e}")
def close_connection(self):
if self.cursor:
self.cursor.close()
self.cursor = None
if self.connection:
self.connection.close()
self.connection = None
View File
+116
View File
@@ -0,0 +1,116 @@
import json
import logging
import sqlite3
import uuid
from typing import Any, Dict, List, Optional
from embedchain.constants import SQLITE_PATH
from embedchain.memory.message import ChatMessage
from embedchain.memory.utils import merge_metadata_dict
CHAT_MESSAGE_CREATE_TABLE_QUERY = """
CREATE TABLE IF NOT EXISTS chat_history (
app_id TEXT,
id TEXT,
question TEXT,
answer TEXT,
metadata TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, app_id)
)
"""
class ECChatMemory:
def __init__(self) -> None:
with sqlite3.connect(SQLITE_PATH) as self.connection:
self.cursor = self.connection.cursor()
self.cursor.execute(CHAT_MESSAGE_CREATE_TABLE_QUERY)
self.connection.commit()
def add(self, app_id, chat_message: ChatMessage) -> Optional[str]:
memory_id = str(uuid.uuid4())
metadata_dict = merge_metadata_dict(chat_message.human_message.metadata, chat_message.ai_message.metadata)
if metadata_dict:
metadata = self._serialize_json(metadata_dict)
ADD_CHAT_MESSAGE_QUERY = """
INSERT INTO chat_history (app_id, id, question, answer, metadata)
VALUES (?, ?, ?, ?, ?)
"""
self.cursor.execute(
ADD_CHAT_MESSAGE_QUERY,
(
app_id,
memory_id,
chat_message.human_message.content,
chat_message.ai_message.content,
metadata if metadata_dict else "{}",
),
)
self.connection.commit()
logging.info(f"Added chat memory to db with id: {memory_id}")
return memory_id
def delete_chat_history(self, app_id: str):
DELETE_CHAT_HISTORY_QUERY = """
DELETE FROM chat_history WHERE app_id=?
"""
self.cursor.execute(
DELETE_CHAT_HISTORY_QUERY,
(app_id,),
)
self.connection.commit()
def get_recent_memories(self, app_id, num_rounds=10, display_format=False) -> List[ChatMessage]:
"""
Get the most recent num_rounds rounds of conversations
between human and AI, for a given app_id.
"""
QUERY = """
SELECT * FROM chat_history
WHERE app_id=?
ORDER BY created_at DESC
LIMIT ?
"""
self.cursor.execute(
QUERY,
(app_id, num_rounds),
)
results = self.cursor.fetchall()
history = []
for result in results:
app_id, _, question, answer, metadata, timestamp = result
metadata = self._deserialize_json(metadata=metadata)
# Return list of dict if display_format is True
if display_format:
history.append({"human": question, "ai": answer, "metadata": metadata, "timestamp": timestamp})
else:
memory = ChatMessage()
memory.add_user_message(question, metadata=metadata)
memory.add_ai_message(answer, metadata=metadata)
history.append(memory)
return history
def _serialize_json(self, metadata: Dict[str, Any]):
return json.dumps(metadata)
def _deserialize_json(self, metadata: str):
return json.loads(metadata)
def close_connection(self):
self.connection.close()
def count_history_messages(self, app_id: str):
QUERY = """
SELECT COUNT(*) FROM chat_history
WHERE app_id=?
"""
self.cursor.execute(
QUERY,
(app_id,),
)
count = self.cursor.fetchone()[0]
return count
+72
View File
@@ -0,0 +1,72 @@
import logging
from typing import Any, Dict, Optional
from embedchain.helper.json_serializable import JSONSerializable
class BaseMessage(JSONSerializable):
"""
The base abstract message class.
Messages are the inputs and outputs of Models.
"""
# The string content of the message.
content: str
# The creator of the message. AI, Human, Bot etc.
by: str
# Any additional info.
metadata: Dict[str, Any]
def __init__(self, content: str, creator: str, metadata: Optional[Dict[str, Any]] = None) -> None:
super().__init__()
self.content = content
self.creator = creator
self.metadata = metadata
@property
def type(self) -> str:
"""Type of the Message, used for serialization."""
@classmethod
def is_lc_serializable(cls) -> bool:
"""Return whether this class is serializable."""
return True
def __str__(self) -> str:
return f"{self.creator}: {self.content}"
class ChatMessage(JSONSerializable):
"""
The base abstract chat message class.
Chat messages are the pair of (question, answer) conversation
between human and model.
"""
human_message: Optional[BaseMessage] = None
ai_message: Optional[BaseMessage] = None
def add_user_message(self, message: str, metadata: Optional[dict] = None):
if self.human_message:
logging.info(
"Human message already exists in the chat message,\
overwritting it with new message."
)
self.human_message = BaseMessage(content=message, creator="human", metadata=metadata)
def add_ai_message(self, message: str, metadata: Optional[dict] = None):
if self.ai_message:
logging.info(
"AI message already exists in the chat message,\
overwritting it with new message."
)
self.ai_message = BaseMessage(content=message, creator="ai", metadata=metadata)
def __str__(self) -> str:
return f"{self.human_message}\n{self.ai_message}"
+35
View File
@@ -0,0 +1,35 @@
from typing import Any, Dict, Optional
def merge_metadata_dict(left: Optional[Dict[str, Any]], right: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
"""
Merge the metadatas of two BaseMessage types.
Args:
left (Dict[str, Any]): metadata of human message
right (Dict[str, Any]): metadata of ai message
Returns:
Dict[str, Any]: combined metadata dict with dedup
to be saved in db.
"""
if not left and not right:
return None
elif not left:
return right
elif not right:
return left
merged = left.copy()
for k, v in right.items():
if k not in merged:
merged[k] = v
elif type(merged[k]) != type(v):
raise ValueError(f'additional_kwargs["{k}"] already exists in this message,' " but with a different type.")
elif isinstance(merged[k], str):
merged[k] += v
elif isinstance(merged[k], dict):
merged[k] = merge_metadata_dict(merged[k], v)
else:
raise ValueError(f"Additional kwargs key {k} already exists in this message.")
return merged
+2
View File
@@ -29,6 +29,7 @@ class IndirectDataType(Enum):
JSON = "json"
OPENAPI = "openapi"
GMAIL = "gmail"
POSTGRES = "postgres"
class SpecialDataType(Enum):
@@ -57,3 +58,4 @@ class DataType(Enum):
JSON = IndirectDataType.JSON.value
OPENAPI = IndirectDataType.OPENAPI.value
GMAIL = IndirectDataType.GMAIL.value
POSTGRES = IndirectDataType.POSTGRES.value
+62 -47
View File
@@ -7,22 +7,22 @@ import uuid
import requests
import yaml
from fastapi import FastAPI, HTTPException
from embedchain import Client
from embedchain.config import PipelineConfig
from embedchain.embedchain import CONFIG_DIR, EmbedChain
from embedchain.config import ChunkerConfig, PipelineConfig
from embedchain.constants import SQLITE_PATH
from embedchain.embedchain import EmbedChain
from embedchain.embedder.base import BaseEmbedder
from embedchain.embedder.openai import OpenAIEmbedder
from embedchain.factory import EmbedderFactory, LlmFactory, VectorDBFactory
from embedchain.helper.json_serializable import register_deserializable
from embedchain.llm.base import BaseLlm
from embedchain.llm.openai import OpenAILlm
from embedchain.telemetry.posthog import AnonymousTelemetry
from embedchain.utils import validate_yaml_config
from embedchain.vectordb.base import BaseVectorDB
from embedchain.vectordb.chroma import ChromaDB
SQLITE_PATH = os.path.join(CONFIG_DIR, "embedchain.db")
@register_deserializable
class Pipeline(EmbedChain):
@@ -41,8 +41,9 @@ class Pipeline(EmbedChain):
embedding_model: BaseEmbedder = None,
llm: BaseLlm = None,
yaml_path: str = None,
log_level=logging.INFO,
log_level=logging.WARN,
auto_deploy: bool = False,
chunker: ChunkerConfig = None,
):
"""
Initialize a new `App` instance.
@@ -57,12 +58,15 @@ class Pipeline(EmbedChain):
:type llm: BaseLlm, optional
:param yaml_path: Path to the YAML configuration file, defaults to None
:type yaml_path: str, optional
:param log_level: Log level to use, defaults to logging.INFO
:param log_level: Log level to use, defaults to logging.WARN
:type log_level: int, optional
:param auto_deploy: Whether to deploy the pipeline automatically, defaults to False
:type auto_deploy: bool, optional
:raises Exception: If an error occurs while creating the pipeline
"""
# Setup user directory if it doesn't exist already
Client.setup_dir()
if id and yaml_path:
raise Exception("Cannot provide both id and config. Please provide only one of them.")
@@ -74,18 +78,18 @@ class Pipeline(EmbedChain):
logging.basicConfig(level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
self.logger = logging.getLogger(__name__)
self.auto_deploy = auto_deploy
# Store the yaml config as an attribute to be able to send it
self.yaml_config = None
self.client = None
# pipeline_id from the backend
self.id = None
self.chunker = None
if chunker:
self.chunker = ChunkerConfig(**chunker)
self.config = config or PipelineConfig()
self.name = self.config.name
self.config.id = self.local_id = str(uuid.uuid4()) if self.config.id is None else self.config.id
if yaml_path:
@@ -109,11 +113,12 @@ class Pipeline(EmbedChain):
self.llm = llm or OpenAILlm()
self._init_db()
# setup user id and directory
self.u_id = self._load_or_generate_user_id()
# Send anonymous telemetry
self._telemetry_props = {"class": self.__class__.__name__}
self.telemetry = AnonymousTelemetry(enabled=self.config.collect_metrics)
# Establish a connection to the SQLite database
self.connection = sqlite3.connect(SQLITE_PATH)
self.connection = sqlite3.connect(SQLITE_PATH, check_same_thread=False)
self.cursor = self.connection.cursor()
# Create the 'data_sources' table if it doesn't exist
@@ -131,8 +136,10 @@ class Pipeline(EmbedChain):
"""
)
self.connection.commit()
# Send anonymous telemetry
self.telemetry.capture(event_name="init", properties=self._telemetry_props)
self.user_asks = [] # legacy defaults
self.user_asks = []
if self.auto_deploy:
self.deploy()
@@ -219,15 +226,29 @@ class Pipeline(EmbedChain):
"""
Search for similar documents related to the query in the vector database.
"""
# Send anonymous telemetry
self.telemetry.capture(event_name="search", properties=self._telemetry_props)
# TODO: Search will call the endpoint rather than fetching the data from the db itself when deploy=True.
if self.id is None:
where = {"app_id": self.local_id}
return self.db.query(
context = self.db.query(
query,
n_results=num_documents,
where=where,
skip_embedding=False,
citations=True,
)
result = []
for c in context:
result.append(
{
"context": c[0],
"source": c[1],
"document_id": c[2],
}
)
return result
else:
# Make API call to the backend to get the results
NotImplementedError("Search is not implemented yet for the prod mode.")
@@ -295,6 +316,15 @@ class Pipeline(EmbedChain):
)
self.connection.commit()
def get_data_sources(self):
db_data = self.cursor.execute("SELECT * FROM data_sources WHERE pipeline_id = ?", (self.local_id,)).fetchall()
data_sources = []
for data in db_data:
data_sources.append({"data_type": data[2], "data_value": data[3], "metadata": data[4]})
return data_sources
def deploy(self):
if self.client is None:
self._init_client()
@@ -312,6 +342,9 @@ class Pipeline(EmbedChain):
data_hash, data_type, data_value = result[1], result[2], result[3]
self._process_and_upload_data(data_hash, data_type, data_value)
# Send anonymous telemetry
self.telemetry.capture(event_name="deploy", properties=self._telemetry_props)
@classmethod
def from_config(cls, yaml_path: str, auto_deploy: bool = False):
"""
@@ -327,10 +360,16 @@ class Pipeline(EmbedChain):
with open(yaml_path, "r") as file:
config_data = yaml.safe_load(file)
pipeline_config_data = config_data.get("pipeline", {}).get("config", {})
try:
validate_yaml_config(config_data)
except Exception as e:
raise Exception(f"❌ Error occurred while validating the YAML config. Error: {str(e)}")
pipeline_config_data = config_data.get("app", {}).get("config", {})
db_config_data = config_data.get("vectordb", {})
embedding_model_config_data = config_data.get("embedding_model", {})
embedding_model_config_data = config_data.get("embedding_model", config_data.get("embedder", {}))
llm_config_data = config_data.get("llm", {})
chunker_config_data = config_data.get("chunker", {})
pipeline_config = PipelineConfig(**pipeline_config_data)
@@ -347,6 +386,11 @@ class Pipeline(EmbedChain):
embedding_model = EmbedderFactory.create(
embedding_model_provider, embedding_model_config_data.get("config", {})
)
# Send anonymous telemetry
event_properties = {"init_type": "yaml_config"}
AnonymousTelemetry().capture(event_name="init", properties=event_properties)
return cls(
config=pipeline_config,
llm=llm,
@@ -354,34 +398,5 @@ class Pipeline(EmbedChain):
embedding_model=embedding_model,
yaml_path=yaml_path,
auto_deploy=auto_deploy,
chunker=chunker_config_data,
)
def start(self, host="0.0.0.0", port=8000):
app = FastAPI()
@app.post("/add")
async def add_document(data_value: str, data_type: str = None):
"""
Add a document to the pipeline.
"""
try:
document = {"data_value": data_value, "data_type": data_type}
self.add(document)
return {"message": "Document added successfully"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/query")
async def query_documents(query: str, num_documents: int = 3):
"""
Query for similar documents in the pipeline.
"""
try:
results = self.search(query, num_documents)
return results
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
import uvicorn
uvicorn.run(app, host=host, port=port)
View File
+140
View File
@@ -0,0 +1,140 @@
import logging
import os
import re
import tempfile
import time
from pathlib import Path
from typing import cast
from openai import OpenAI
from openai.types.beta.threads import MessageContentText, ThreadMessage
from embedchain.config import AddConfig
from embedchain.data_formatter import DataFormatter
from embedchain.models.data_type import DataType
from embedchain.telemetry.posthog import AnonymousTelemetry
from embedchain.utils import detect_datatype
logging.basicConfig(level=logging.WARN)
class OpenAIAssistant:
def __init__(
self,
name=None,
instructions=None,
tools=None,
thread_id=None,
model="gpt-4-1106-preview",
data_sources=None,
assistant_id=None,
log_level=logging.WARN,
collect_metrics=True,
):
self.name = name or "OpenAI Assistant"
self.instructions = instructions
self.tools = tools or [{"type": "retrieval"}]
self.model = model
self.data_sources = data_sources or []
self.log_level = log_level
self._client = OpenAI()
self._initialize_assistant(assistant_id)
self.thread_id = thread_id or self._create_thread()
self._telemetry_props = {"class": self.__class__.__name__}
self.telemetry = AnonymousTelemetry(enabled=collect_metrics)
self.telemetry.capture(event_name="init", properties=self._telemetry_props)
def add(self, source, data_type=None):
file_path = self._prepare_source_path(source, data_type)
self._add_file_to_assistant(file_path)
event_props = {
**self._telemetry_props,
"data_type": data_type or detect_datatype(source),
}
self.telemetry.capture(event_name="add", properties=event_props)
logging.info("Data successfully added to the assistant.")
def chat(self, message):
self._send_message(message)
self.telemetry.capture(event_name="chat", properties=self._telemetry_props)
return self._get_latest_response()
def delete_thread(self):
self._client.beta.threads.delete(self.thread_id)
self.thread_id = self._create_thread()
# Internal methods
def _initialize_assistant(self, assistant_id):
file_ids = self._generate_file_ids(self.data_sources)
self.assistant = (
self._client.beta.assistants.retrieve(assistant_id)
if assistant_id
else self._client.beta.assistants.create(
name=self.name, model=self.model, file_ids=file_ids, instructions=self.instructions, tools=self.tools
)
)
def _create_thread(self):
thread = self._client.beta.threads.create()
return thread.id
def _prepare_source_path(self, source, data_type=None):
if Path(source).is_file():
return source
data_type = data_type or detect_datatype(source)
formatter = DataFormatter(data_type=DataType(data_type), config=AddConfig(), kwargs={})
data = formatter.loader.load_data(source)["data"]
return self._save_temp_data(data=data[0]["content"].encode(), source=source)
def _add_file_to_assistant(self, file_path):
file_obj = self._client.files.create(file=open(file_path, "rb"), purpose="assistants")
self._client.beta.assistants.files.create(assistant_id=self.assistant.id, file_id=file_obj.id)
def _generate_file_ids(self, data_sources):
return [
self._add_file_to_assistant(self._prepare_source_path(ds["source"], ds.get("data_type")))
for ds in data_sources
]
def _send_message(self, message):
self._client.beta.threads.messages.create(thread_id=self.thread_id, role="user", content=message)
self._wait_for_completion()
def _wait_for_completion(self):
run = self._client.beta.threads.runs.create(
thread_id=self.thread_id,
assistant_id=self.assistant.id,
instructions=self.instructions,
)
run_id = run.id
run_status = run.status
while run_status in ["queued", "in_progress", "requires_action"]:
time.sleep(0.1) # Sleep before making the next API call to avoid hitting rate limits
run = self._client.beta.threads.runs.retrieve(thread_id=self.thread_id, run_id=run_id)
run_status = run.status
if run_status == "failed":
raise ValueError(f"Thread run failed with the following error: {run.last_error}")
def _get_latest_response(self):
history = self._get_history()
return self._format_message(history[0]) if history else None
def _get_history(self):
messages = self._client.beta.threads.messages.list(thread_id=self.thread_id, order="desc")
return list(messages)
def _format_message(self, thread_message):
thread_message = cast(ThreadMessage, thread_message)
content = [c.text.value for c in thread_message.content if isinstance(c, MessageContentText)]
return " ".join(content)
def _save_temp_data(self, data, source):
special_chars_pattern = r'[\\/:*?"<>|&=% ]+'
sanitized_source = re.sub(special_chars_pattern, "_", source)[:256]
temp_dir = tempfile.mkdtemp()
file_path = os.path.join(temp_dir, sanitized_source)
with open(file_path, "wb") as file:
file.write(data)
return file_path
View File
+67
View File
@@ -0,0 +1,67 @@
import json
import logging
import os
import uuid
from pathlib import Path
from posthog import Posthog
import embedchain
HOME_DIR = str(Path.home())
CONFIG_DIR = os.path.join(HOME_DIR, ".embedchain")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
logger = logging.getLogger(__name__)
class AnonymousTelemetry:
def __init__(self, host="https://app.posthog.com", enabled=True):
self.project_api_key = "phc_PHQDA5KwztijnSojsxJ2c1DuJd52QCzJzT2xnSGvjN2"
self.host = host
self.posthog = Posthog(project_api_key=self.project_api_key, host=self.host)
self.user_id = self.get_user_id()
self.enabled = enabled
# Check if telemetry tracking is disabled via environment variable
if "EC_TELEMETRY" in os.environ and os.environ["EC_TELEMETRY"].lower() not in [
"1",
"true",
"yes",
]:
self.enabled = False
if not self.enabled:
self.posthog.disabled = True
# Silence posthog logging
posthog_logger = logging.getLogger("posthog")
posthog_logger.disabled = True
def get_user_id(self):
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r") as f:
data = json.load(f)
if "user_id" in data:
return data["user_id"]
user_id = str(uuid.uuid4())
with open(CONFIG_FILE, "w") as f:
json.dump({"user_id": user_id}, f)
return user_id
def capture(self, event_name, properties=None):
default_properties = {
"version": embedchain.__version__,
"language": "python",
"pid": os.getpid(),
}
properties.update(default_properties)
try:
self.posthog.capture(self.user_id, event_name, properties)
except Exception:
logger.exception(f"Failed to send telemetry {event_name=}")
+90
View File
@@ -1,9 +1,12 @@
import json
import logging
import os
import re
import string
from typing import Any
from schema import Optional, Or, Schema
from embedchain.models.data_type import DataType
@@ -261,6 +264,93 @@ def detect_datatype(source: Any) -> DataType:
# TODO: check if source is gmail query
# check if the source is valid json string
if is_valid_json_string(source):
logging.debug(f"Source of `{formatted_source}` detected as `json`.")
return DataType.JSON
# Use text as final fallback.
logging.debug(f"Source of `{formatted_source}` detected as `text`.")
return DataType.TEXT
# check if the source is valid json string
def is_valid_json_string(source: str):
try:
_ = json.loads(source)
return True
except json.JSONDecodeError:
logging.error(
"Insert valid string format of JSON. \
Check the docs to see the supported formats - `https://docs.embedchain.ai/data-sources/json`"
)
return False
def validate_yaml_config(config_data):
schema = Schema(
{
Optional("app"): {
Optional("config"): {
Optional("id"): str,
Optional("name"): str,
Optional("log_level"): Or("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
Optional("collect_metrics"): bool,
Optional("collection_name"): str,
}
},
Optional("llm"): {
Optional("provider"): Or(
"openai",
"azure_openai",
"anthropic",
"huggingface",
"cohere",
"gpt4all",
"jina",
"llama2",
"vertexai",
),
Optional("config"): {
Optional("model"): str,
Optional("number_documents"): int,
Optional("temperature"): float,
Optional("max_tokens"): int,
Optional("top_p"): Or(float, int),
Optional("stream"): bool,
Optional("template"): str,
Optional("system_prompt"): str,
Optional("deployment_name"): str,
Optional("where"): dict,
Optional("query_type"): str,
},
},
Optional("vectordb"): {
Optional("provider"): Or(
"chroma", "elasticsearch", "opensearch", "pinecone", "qdrant", "weaviate", "zilliz"
),
Optional("config"): object, # TODO: add particular config schema for each provider
},
Optional("embedder"): {
Optional("provider"): Or("openai", "gpt4all", "huggingface", "vertexai", "azure_openai"),
Optional("config"): {
Optional("model"): Optional(str),
Optional("deployment_name"): Optional(str),
},
},
Optional("embedding_model"): {
Optional("provider"): Or("openai", "gpt4all", "huggingface", "vertexai", "azure_openai"),
Optional("config"): {
Optional("model"): str,
Optional("deployment_name"): str,
},
},
Optional("chunker"): {
Optional("chunk_size"): int,
Optional("chunk_overlap"): int,
Optional("length_function"): str,
},
}
)
return schema.validate(config_data)
+25 -14
View File
@@ -1,5 +1,5 @@
import logging
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union
from chromadb import Collection, QueryResult
from langchain.docstore.document import Document
@@ -38,7 +38,7 @@ class ChromaDB(BaseVectorDB):
else:
self.config = ChromaDbConfig()
self.settings = Settings()
self.settings = Settings(anonymized_telemetry=False)
self.settings.allow_reset = self.config.allow_reset if hasattr(self.config, "allow_reset") else False
if self.config.chroma_settings:
for key, value in self.config.chroma_settings.items():
@@ -77,7 +77,7 @@ class ChromaDB(BaseVectorDB):
def _generate_where_clause(self, where: Dict[str, any]) -> str:
# If only one filter is supplied, return it as is
# (no need to wrap in $and based on chroma docs)
if len(where.keys()) == 1:
if len(where.keys()) <= 1:
return where
where_filters = []
for k, v in where.items():
@@ -192,8 +192,13 @@ class ChromaDB(BaseVectorDB):
]
def query(
self, input_query: List[str], n_results: int, where: Dict[str, any], skip_embedding: bool
) -> List[Tuple[str, str, str]]:
self,
input_query: List[str],
n_results: int,
where: Dict[str, any],
skip_embedding: bool,
citations: bool = False,
) -> Union[List[Tuple[str, str, str]], List[str]]:
"""
Query contents from vector database based on vector similarity
@@ -205,9 +210,12 @@ class ChromaDB(BaseVectorDB):
:type where: Dict[str, Any]
:param skip_embedding: Optional. If True, then the input_query is assumed to be already embedded.
:type skip_embedding: bool
:param citations: we use citations boolean param to return context along with the answer.
:type citations: bool, default is False.
:raises InvalidDimensionException: Dimensions do not match.
:return: The content of the document that matched your query, url of the source, doc_id
:rtype: List[Tuple[str,str,str]]
:return: The content of the document that matched your query,
along with url of the source and doc_id (if citations flag is true)
:rtype: List[str], if citations=False, otherwise List[Tuple[str, str, str]]
"""
try:
if skip_embedding:
@@ -216,7 +224,7 @@ class ChromaDB(BaseVectorDB):
input_query,
],
n_results=n_results,
where=where,
where=self._generate_where_clause(where),
)
else:
result = self.collection.query(
@@ -224,7 +232,7 @@ class ChromaDB(BaseVectorDB):
input_query,
],
n_results=n_results,
where=where,
where=self._generate_where_clause(where),
)
except InvalidDimensionException as e:
raise InvalidDimensionException(
@@ -236,10 +244,13 @@ class ChromaDB(BaseVectorDB):
contexts = []
for result in results_formatted:
context = result[0].page_content
metadata = result[0].metadata
source = metadata["url"]
doc_id = metadata["doc_id"]
contexts.append((context, source, doc_id))
if citations:
metadata = result[0].metadata
source = metadata["url"]
doc_id = metadata["doc_id"]
contexts.append((context, source, doc_id))
else:
contexts.append(context)
return contexts
def set_collection_name(self, name: str):
@@ -264,7 +275,7 @@ class ChromaDB(BaseVectorDB):
return self.collection.count()
def delete(self, where):
return self.collection.delete(where=where)
return self.collection.delete(where=self._generate_where_clause(where))
def reset(self):
"""
+22 -11
View File
@@ -1,5 +1,5 @@
import logging
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union
try:
from elasticsearch import Elasticsearch
@@ -136,8 +136,13 @@ class ElasticsearchDB(BaseVectorDB):
self.client.indices.refresh(index=self._get_index())
def query(
self, input_query: List[str], n_results: int, where: Dict[str, any], skip_embedding: bool
) -> List[Tuple[str, str, str]]:
self,
input_query: List[str],
n_results: int,
where: Dict[str, any],
skip_embedding: bool,
citations: bool = False,
) -> Union[List[Tuple[str, str, str]], List[str]]:
"""
query contents from vector data base based on vector similarity
@@ -150,8 +155,11 @@ class ElasticsearchDB(BaseVectorDB):
:param skip_embedding: Optional. If True, then the input_query is assumed to be already embedded.
:type skip_embedding: bool
:return: The context of the document that matched your query, url of the source, doc_id
:rtype: List[Tuple[str,str,str]]
:param citations: we use citations boolean param to return context along with the answer.
:type citations: bool, default is False.
:return: The content of the document that matched your query,
along with url of the source and doc_id (if citations flag is true)
:rtype: List[str], if citations=False, otherwise List[Tuple[str, str, str]]
"""
if skip_embedding:
query_vector = input_query
@@ -175,14 +183,17 @@ class ElasticsearchDB(BaseVectorDB):
_source = ["text", "metadata.url", "metadata.doc_id"]
response = self.client.search(index=self._get_index(), query=query, _source=_source, size=n_results)
docs = response["hits"]["hits"]
contents = []
contexts = []
for doc in docs:
context = doc["_source"]["text"]
metadata = doc["_source"]["metadata"]
source = metadata["url"]
doc_id = metadata["doc_id"]
contents.append(tuple((context, source, doc_id)))
return contents
if citations:
metadata = doc["_source"]["metadata"]
source = metadata["url"]
doc_id = metadata["doc_id"]
contexts.append(tuple((context, source, doc_id)))
else:
contexts.append(context)
return contexts
def set_collection_name(self, name: str):
"""
+21 -10
View File
@@ -1,5 +1,5 @@
import logging
from typing import Dict, List, Optional, Set, Tuple
from typing import Dict, List, Optional, Set, Tuple, Union
try:
from opensearchpy import OpenSearch
@@ -146,8 +146,13 @@ class OpenSearchDB(BaseVectorDB):
self.client.indices.refresh(index=self._get_index())
def query(
self, input_query: List[str], n_results: int, where: Dict[str, any], skip_embedding: bool
) -> List[Tuple[str, str, str]]:
self,
input_query: List[str],
n_results: int,
where: Dict[str, any],
skip_embedding: bool,
citations: bool = False,
) -> Union[List[Tuple[str, str, str]], List[str]]:
"""
query contents from vector data base based on vector similarity
@@ -159,8 +164,11 @@ class OpenSearchDB(BaseVectorDB):
:type where: Dict[str, any]
:param skip_embedding: Optional. If True, then the input_query is assumed to be already embedded.
:type skip_embedding: bool
:return: The content of the document that matched your query, url of the source, doc_id
:rtype: List[Tuple[str,str,str]]
:param citations: we use citations boolean param to return context along with the answer.
:type citations: bool, default is False.
:return: The content of the document that matched your query,
along with url of the source and doc_id (if citations flag is true)
:rtype: List[str], if citations=False, otherwise List[Tuple[str, str, str]]
"""
# TODO(rupeshbansal, deshraj): Add support for skip embeddings here if already exists
embeddings = OpenAIEmbeddings()
@@ -188,13 +196,16 @@ class OpenSearchDB(BaseVectorDB):
k=n_results,
)
contents = []
contexts = []
for doc in docs:
context = doc.page_content
source = doc.metadata["url"]
doc_id = doc.metadata["doc_id"]
contents.append(tuple((context, source, doc_id)))
return contents
if citations:
source = doc.metadata["url"]
doc_id = doc.metadata["doc_id"]
contexts.append(tuple((context, source, doc_id)))
else:
contexts.append(context)
return contexts
def set_collection_name(self, name: str):
"""
+21 -10
View File
@@ -1,5 +1,5 @@
import os
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Optional, Tuple, Union
try:
import pinecone
@@ -119,8 +119,13 @@ class PineconeDB(BaseVectorDB):
self.client.upsert(docs[i : i + self.BATCH_SIZE])
def query(
self, input_query: List[str], n_results: int, where: Dict[str, any], skip_embedding: bool
) -> List[Tuple[str, str, str]]:
self,
input_query: List[str],
n_results: int,
where: Dict[str, any],
skip_embedding: bool,
citations: bool = False,
) -> Union[List[Tuple[str, str, str]], List[str]]:
"""
query contents from vector database based on vector similarity
:param input_query: list of query string
@@ -131,22 +136,28 @@ class PineconeDB(BaseVectorDB):
:type where: Dict[str, any]
:param skip_embedding: Optional. if True, input_query is already embedded
:type skip_embedding: bool
:return: The content of the document that matched your query, url of the source, doc_id
:rtype: List[Tuple[str,str,str]]
:param citations: we use citations boolean param to return context along with the answer.
:type citations: bool, default is False.
:return: The content of the document that matched your query,
along with url of the source and doc_id (if citations flag is true)
:rtype: List[str], if citations=False, otherwise List[Tuple[str, str, str]]
"""
if not skip_embedding:
query_vector = self.embedder.embedding_fn([input_query])[0]
else:
query_vector = input_query
data = self.client.query(vector=query_vector, filter=where, top_k=n_results, include_metadata=True)
contents = []
contexts = []
for doc in data["matches"]:
metadata = doc["metadata"]
context = metadata["text"]
source = metadata["url"]
doc_id = metadata["doc_id"]
contents.append(tuple((context, source, doc_id)))
return contents
if citations:
source = metadata["url"]
doc_id = metadata["doc_id"]
contexts.append(tuple((context, source, doc_id)))
else:
contexts.append(context)
return contexts
def set_collection_name(self, name: str):
"""

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