@@ -0,0 +1,66 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: What happened and how to reproduce it?
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Tell us what you see! It would be nice if you could leave us the logs.
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-did-you-expect
|
||||
attributes:
|
||||
label: What did you expect to happen?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Describe your expectations.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: pathway_version
|
||||
attributes:
|
||||
label: Version
|
||||
description: On which version of the Pathway Live Data Framework are you working?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: docker_version
|
||||
attributes:
|
||||
label: Docker Versions (if used)
|
||||
description: On which Docker version are you working?
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: os_version
|
||||
attributes:
|
||||
label: OS
|
||||
description: On which OS did you run the Pathway Live Data Framework?
|
||||
options:
|
||||
- Linux
|
||||
- MacOS
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: cpu_architecture
|
||||
attributes:
|
||||
label: On which CPU architecture did you run the Pathway Live Data Framework?
|
||||
multiple: false
|
||||
options:
|
||||
- x86-64
|
||||
- ARM64 (AArch64, Apple silicon)
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: New feature? Tell us about it on GitHub Discussions
|
||||
url: https://github.com/pathwaycom/pathway/discussions
|
||||
about: We are using Discussions as a place to connect with other members of the Pathway community.
|
||||
- name: Discord
|
||||
url: https://discord.com/invite/pathway
|
||||
about: Ask questions you are wondering about.
|
||||
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,27 @@
|
||||
### Introduction
|
||||
To contribute code to the Pathway Live Data Framework project, start by discussing your proposed changes on Discord or by filing an issue.
|
||||
Once approved, follow the fork + pull request model against the main branch, ensuring you've signed the contributor license agreement.
|
||||
|
||||
### Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
|
||||
### How has this been tested?
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
|
||||
### Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature or improvement (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
|
||||
### Related issue(s):
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] My code follows the code style of this project,
|
||||
- [ ] My change requires a change to the documentation,
|
||||
- [ ] I described the modification in the CHANGELOG.md file.
|
||||
@@ -0,0 +1,33 @@
|
||||
name: lint PR
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main'}}
|
||||
jobs:
|
||||
linter:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
- name: Install poetry
|
||||
uses: abatilo/actions-poetry@v3
|
||||
- name: Install linters
|
||||
run: poetry install --no-root --only linters
|
||||
- name: Run black
|
||||
run: poetry run black --check .
|
||||
- name: Run Flake8
|
||||
run: poetry run flake8 .
|
||||
- name: Run mypy
|
||||
run: poetry run mypy .
|
||||
- name: Run isort
|
||||
run: poetry run isort --check .
|
||||
@@ -0,0 +1,165 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
**/.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
pw-env/
|
||||
|
||||
# llm debug
|
||||
examples/ui/data/*
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"python.defaultInterpreterPath": "${env:HOME}/pw-env/bin/python",
|
||||
"python.formatting.provider": "none",
|
||||
"editor.formatOnSave": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.mypyEnabled": true,
|
||||
"[python]": {
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"editor.rulers": [88],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
},
|
||||
"git.autofetchPeriod": 315360000
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
# Pathway Code of Conduct
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, health condition, ethnicity, gender identity and expression, level of
|
||||
experience, nationality, country of origin, personal appearance, race, religion, or sexual identity
|
||||
and orientation, and any other criteria falling under discriminatory practices under the Law of France.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language.
|
||||
* Being respectful of differing viewpoints and experiences.
|
||||
* Gracefully accepting constructive criticism.
|
||||
* Focusing on what is best for the community.
|
||||
* Showing empathy towards other community members.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances.
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks.
|
||||
* Public or private harassment.
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission.
|
||||
* Conduct which could reasonably be considered inappropriate for the forum in
|
||||
which it occurs.
|
||||
|
||||
All Pathway forums and spaces are meant for professional interactions, and any behavior which could reasonably be considered inappropriate in a professional setting is unacceptable.
|
||||
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies to all content on pathway.com, Pathway’s GitHub organization, or any other official Pathway web presence allowing for community interactions, as well as at all official Pathway events, whether offline or online.
|
||||
|
||||
The Code of Conduct also applies within project spaces and in public spaces whenever an individual is representing Pathway or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed or de facto representative at an online or offline event.
|
||||
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
Conflicts in an open source project can take many forms, from someone having a bad day and using harsh and hurtful language in the issue queue, to more serious instances such as sexist/racist statements or threats of violence, and everything in between.
|
||||
|
||||
If the behavior is threatening or harassing, or for other reasons requires immediate escalation, please see below.
|
||||
|
||||
However, for the vast majority of issues, we aim to empower individuals to first resolve conflicts themselves, asking for help when needed, and only after that fails to escalate further. This approach gives people more control over the outcome of their dispute.
|
||||
|
||||
If you are experiencing or witnessing conflict, we ask you to use the following escalation strategy to address the conflict:
|
||||
|
||||
1. If you so wish and if you do not feel threatened or at risk of any form of personal abuse, address the perceived conflict directly with those involved, preferably in a real-time medium.
|
||||
2. If this fails, get a third party (e.g. a mutual friend, and/or someone with
|
||||
background on the issue, but not involved in the conflict) to intercede.
|
||||
3. If you are still unable to resolve the conflict, and you believe it rises to
|
||||
harassment or another code of conduct violation, report it.
|
||||
|
||||
Please note that if you are experiencing or witnessing a discriminatory practice that would be susceptible to be condemned by law, we ask you to directly escalate to 3.
|
||||
|
||||
## Reporting Violations
|
||||
|
||||
Violations of the Code of Conduct can be reported to Pathway via email to code_of_conduct@pathway.com. Project maintainers will determine whether the Code of Conduct was violated, and will issue an appropriate sanction, possibly including a written warning or expulsion from the project, project sponsored spaces, or project forums. We ask that you make a good-faith effort to resolve your conflict via the conflict resolution policy before submitting a report.
|
||||
|
||||
Violations of the Code of Conduct can occur in any setting, even those unrelated to the project. We will only consider complaints about conduct that has occurred within one year of the report.
|
||||
|
||||
|
||||
## Enforcement
|
||||
|
||||
If the Project maintainers receive a report alleging a violation of the Code of Conduct, the Project maintainers will notify the accused of the report, and provide them an opportunity to discuss the report before a sanction is issued. The Project maintainers will do their utmost to keep the reporter anonymous. If the act is ongoing (such as someone engaging in harassment), or involves a threat to anyone's safety (e.g. threats of violence), the Project maintainers may issue sanctions without notice.
|
||||
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Tensorflow Code of Conduct, and based on Contributor Covenant, version 1.4, available at https://contributor-covenant.org/version/1/4, and includes some aspects of the Geek Feminism Code of Conduct and the Drupal Code of Conduct.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Contributing to the Pathway Live Data Framework
|
||||
|
||||
Welcome! This guide is intended to help developers that are new to the community
|
||||
to make contributions to the Pathway Live Data Framework project. Please be sure to also read our [code of conduct](CODE_OF_CONDUCT.md). We work hard to make this a welcoming community for all, and we're excited to have you on board!
|
||||
|
||||
## The basics:
|
||||
|
||||
* Use the [Issue Tracker](https://github.com/pathwaycom/llm-app/issues) to
|
||||
report bugs, crashes, performance issues, etc... Please include as much detail
|
||||
|
||||
as possible.
|
||||
|
||||
* [Discussions](https://github.com/pathwaycom/pathway/discussions) is a great
|
||||
venue to ask questions, start a design discussion, or post an RFC.
|
||||
|
||||
* [Discord](https://discord.com/invite/pathway) is our main gathering place - jump in and ask us anything!
|
||||
|
||||
## Contributing Code
|
||||
|
||||
We are eager to build the Pathway Live Data Framework together with the community and are excited to have you here!
|
||||
|
||||
Important: Before you contribute any changes to our repositories (make a Pull Request) please get in touch with us and explain what you want to work on. You can do so by filing an issue, or asking us on Discord. That way you can get coding advice and speedier reviews. If you start to work on an issue before hearing back from a Pathway core team engineer there is a chance that we will not be able to accept your changes - something we all want to avoid!
|
||||
|
||||
We use a standard GitHub fork + pull request model for merging and reviewing
|
||||
changes. New pull requests should be made against the main upstream branch.
|
||||
After a pull request is opened it will be reviewed, and merged after
|
||||
passing continuous integration tests and being accepted by a project or
|
||||
sub-system maintainer.
|
||||
|
||||
We ask that developers sign our [contributor license
|
||||
agreement](https://cla-assistant.io/pathwaycom/llm-app). The
|
||||
process of signing the CLA is automated, and you'll be prompted with instructions
|
||||
the first time you submit a pull request to the project.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Pathway
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,89 @@
|
||||
<div align="center">
|
||||
|
||||
# Pathway Live Data Framework AI Pipelines
|
||||
|
||||
<a href="https://trendshift.io/repositories/4400" target="_blank"><img src="https://trendshift.io/api/badge/repositories/4400" alt="pathwaycom%2Fllm-app | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||

|
||||

|
||||
[](https://discord.gg/pathway)
|
||||
[](https://x.com/intent/follow?screen_name=pathway_com)
|
||||
</div>
|
||||
|
||||
The Pathway Live Data Framework's **AI Pipelines** allow you to quickly put in production AI applications that offer **high-accuracy RAG and AI enterprise search at scale** using the most **up-to-date knowledge** available in your data sources. It provides you ready-to-deploy **LLM (Large Language Model) App Templates**. You can test them on your own machine and deploy on-cloud (GCP, AWS, Azure, Render,...) or on-premises.
|
||||
|
||||
The apps connect and sync (all new data additions, deletions, updates) with data sources on your **file system, Google Drive, Sharepoint, S3, Kafka, PostgreSQL, real-time data APIs**. They come with no infrastructure dependencies that would need a separate setup. They include **built-in data indexing** enabling vector search, hybrid search, and full-text search - all done in-memory, with cache.
|
||||
|
||||
|
||||
## Application Templates
|
||||
|
||||
The application templates provided in this repo scale up to **millions of pages of documents**. Some of them are optimized for simplicity, some are optimized for amazing accuracy. Pick the one that suits you best. You can use it out of the box, or change some steps of the pipeline - for example, if you would like to add a new data source, or change a Vector Index into a Hybrid Index, it's just a one-line change.
|
||||
|
||||
| Application (template) | Description |
|
||||
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`Question-Answering RAG App`](templates/question_answering_rag/) | Basic end-to-end RAG app. A question-answering pipeline that uses the GPT model of choice to provide answers to queries to your documents (PDF, DOCX,...) on a live connected data source (files, Google Drive, Sharepoint,...). You can also try out a [demo REST endpoint](https://pathway.com/solutions/rag-pipelines#try-it-out). |
|
||||
| [`Live Document Indexing (Vector Store / Retriever)`](templates/document_indexing/) | A real-time document indexing pipeline for RAG that acts as a vector store service. It performs live indexing on your documents (PDF, DOCX,...) from a connected data source (files, Google Drive, Sharepoint,...). It can be used with any frontend, or integrated as a retriever backend for a [Langchain](https://pathway.com/blog/langchain-integration) or [Llamaindex](https://pathway.com/blog/llamaindex-pathway) application. You can also try out a [demo REST endpoint](https://pathway.com/solutions/ai-contract-management#try-it-out). |
|
||||
| [`Multimodal RAG pipeline with GPT4o`](templates/multimodal_rag/) | Multimodal RAG using GPT-4o in the parsing stage to index PDFs and other documents from a connected data source files, Google Drive, Sharepoint,...). It is perfect for extracting information from unstructured financial documents in your folders (including charts and tables), updating results as documents change or new ones arrive.|
|
||||
| [`Unstructured-to-SQL pipeline + SQL question-answering`](templates/unstructured_to_sql_on_the_fly/) | A RAG example which connects to unstructured financial data sources (financial report PDFs), structures the data into SQL, and loads it into a PostgreSQL table. It also answers natural language user queries to these financial documents by translating them into SQL using an LLM and executing the query on the PostgreSQL table. |
|
||||
| [`Adaptive RAG App`](templates/adaptive_rag/) | A RAG application using Adaptive RAG, a technique developed by Pathway to reduce token cost in RAG up to 4x while maintaining accuracy. |
|
||||
| [`Private RAG App with Mistral and Ollama`](templates/private_rag/) | A fully private (local) version of the `question_answering_rag` RAG pipeline using Pathway Live Data Framework, Mistral, and Ollama. |
|
||||
| [`Slides AI Search App`](templates/slides_ai_search/) | An indexing pipeline for retrieving slides. It performs multi-modal of PowerPoint and PDF and maintains live index of your slides."|
|
||||
| [`Video RAG with TwelveLabs`](templates/video_rag_twelvelabs/) | A RAG pipeline over **video**. It uses [TwelveLabs](https://twelvelabs.io) Pegasus to turn videos into rich text descriptions and Marengo multimodal embeddings to index them, so you can ask questions about your videos on a live connected data source (files, Google Drive,...). |
|
||||
|
||||
|
||||
## How do these AI Pipelines work?
|
||||
|
||||
The apps can be run as **Docker containers**, and expose an **HTTP API** to connect the frontend. To allow quick testing and demos, some app templates also include an optional Streamlit UI which connects to this API.
|
||||
|
||||
The apps rely on the [Pathway Live Data Framework](https://github.com/pathwaycom/pathway) for data source synchronization and for serving API requests (Pathway is a standalone Python library with a Rust engine built into it). They bring you a **simple and unified application logic** for back-end, embedding, retrieval, LLM tech stack. There is no need to integrate and maintain separate modules for your Gen AI app: ~Vector Database (e.g. Pinecone/Weaviate/Qdrant) + Cache (e.g. Redis) + API Framework (e.g. Fast API)~. Pathway's default choice of **built-in vector index** is based on the lightning-fast [usearch](https://github.com/unum-cloud/usearch) library, and **hybrid full-text indexes** make use of [Tantivy](https://github.com/quickwit-oss/tantivy) library. Everything works out of the box.
|
||||
|
||||
## Getting started
|
||||
|
||||
Each of the [App templates](templates/) in this repo contains a README.md with instructions on how to run it.
|
||||
|
||||
You can also find [more ready-to-run code templates](https://pathway.com/developers/templates/) on the Pathway website.
|
||||
|
||||
|
||||
## Some visual highlights
|
||||
|
||||
Effortlessly extract and organize table and chart data from PDFs, docs, and more with multimodal RAG - in real-time:
|
||||
|
||||

|
||||
|
||||
(Check out [`Multimodal RAG pipeline with GPT4o`](templates/multimodal_rag/) to see the whole pipeline in the works. You may also check out the [`Unstructured-to-SQL pipeline`](templates/unstructured_to_sql_on_the_fly/) for a minimal example that works with non-multimodal models as well.)
|
||||
|
||||
|
||||
Automated real-time knowledge mining and alerting:
|
||||
|
||||

|
||||
|
||||
(Check out the [`Alerting when answers change on Google Drive`](https://github.com/pathwaycom/llm-app/tree/main/templates/drive_alert) app example.)
|
||||
|
||||
|
||||
### Do-it-Yourself Videos
|
||||
|
||||
▶️ [An introduction to building LLM apps with Pathway Live Data Framework](https://www.youtube.com/watch?v=kcrJSk00duw) - by [Jan Chorowski](https://scholar.google.com/citations?user=Yc94070AAAAJ)
|
||||
|
||||
▶️ [Let's build a real-world LLM app in 11 minutes](https://www.youtube.com/watch?v=k1XGo7ts4tI) - by [Pau Labarta Bajo](https://substack.com/@paulabartabajo)
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/pathwaycom/pathway/issues).
|
||||
|
||||
## Contributing
|
||||
|
||||
Anyone who wishes to contribute to this project, whether documentation, features, bug fixes, code cleanup, testing, or code reviews, is very much encouraged to do so. If this is your first contribution to a GitHub project, here is a [Get Started Guide](https://docs.github.com/en/get-started/quickstart/contributing-to-projects).
|
||||
|
||||
If you'd like to make a contribution that needs some more work, just raise your hand on the [Pathway Discord server](https://discord.com/invite/pathway) (#get-help) and let us know what you are planning!
|
||||
|
||||
## Supported and maintained by
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/pathwaycom/"><img src="https://pathway.com/logo-light.svg" alt="Pathway"/></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://pathway.com/solutions/llm-app">
|
||||
<img src="https://img.shields.io/badge/See%20Pathway's%20offering%20for%20AI%20applications-0000FF" alt="See Pathway's offering for AI applications"/>
|
||||
</a>
|
||||
</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`pathwaycom/llm-app`
|
||||
- 原始仓库:https://github.com/pathwaycom/llm-app
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
|
||||
<title>Icon-Architecture/64/Arch_AWS-Fargate_64</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="0%" y1="100%" x2="100%" y2="0%" id="linearGradient-1">
|
||||
<stop stop-color="#C8511B" offset="0%"></stop>
|
||||
<stop stop-color="#FF9900" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Icon-Architecture/64/Arch_AWS-Fargate_64" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Icon-Architecture-BG/64/Compute" fill="url(#linearGradient-1)">
|
||||
<rect id="Rectangle" x="0" y="0" width="80" height="80"></rect>
|
||||
</g>
|
||||
<g id="Icon-Service/64/AWS-Fargate" transform="translate(8.000000, 8.000000)" fill="#FFFFFF">
|
||||
<path d="M44,48.523 L44,43.677 L47,42.477 L47,47.323 L44,48.523 Z M39,42.477 L42,43.677 L42,48.523 L39,47.323 L39,42.477 Z M33,55.523 L33,50.677 L36,49.477 L36,54.323 L33,55.523 Z M28,49.477 L31,50.677 L31,55.523 L28,54.323 L28,49.477 Z M22,48.523 L22,43.677 L25,42.477 L25,47.323 L22,48.523 Z M17,42.477 L20,43.677 L20,48.523 L17,47.323 L17,42.477 Z M21,40.077 L23.308,41 L21,41.923 L18.692,41 L21,40.077 Z M32,47.077 L34.308,48 L32,48.923 L29.692,48 L32,47.077 Z M43,40.077 L45.308,41 L43,41.923 L40.692,41 L43,40.077 Z M48.371,40.071 L43.371,38.071 C43.133,37.977 42.867,37.977 42.629,38.071 L37.629,40.071 C37.249,40.224 37,40.591 37,41 L37,46.923 L32.371,45.071 C32.133,44.977 31.867,44.977 31.629,45.071 L27,46.923 L27,41 C27,40.591 26.751,40.224 26.371,40.071 L21.371,38.071 C21.133,37.977 20.867,37.977 20.629,38.071 L15.629,40.071 C15.249,40.224 15,40.591 15,41 L15,48 C15,48.409 15.249,48.776 15.629,48.929 L20.629,50.929 C20.748,50.976 20.874,51 21,51 C21.126,51 21.252,50.976 21.371,50.929 L26,49.077 L26,55 C26,55.409 26.249,55.776 26.629,55.929 L31.629,57.929 C31.748,57.976 31.874,58 32,58 C32.126,58 32.252,57.976 32.371,57.929 L37.371,55.929 C37.751,55.776 38,55.409 38,55 L38,49.077 L42.629,50.929 C42.748,50.976 42.874,51 43,51 C43.126,51 43.252,50.976 43.371,50.929 L48.371,48.929 C48.751,48.776 49,48.409 49,48 L49,41 C49,40.591 48.751,40.224 48.371,40.071 L48.371,40.071 Z M57,27.938 C57,33.783 44.119,36.938 32,36.938 C19.881,36.938 7,33.783 7,27.938 C7,25.147 10.095,22.69 15.715,21.019 L16.285,22.936 C11.791,24.272 9,26.189 9,27.938 C9,31.249 18.445,34.938 32,34.938 C45.555,34.938 55,31.249 55,27.938 C55,26.189 52.209,24.272 47.715,22.936 L48.285,21.019 C53.905,22.69 57,25.147 57,27.938 L57,27.938 Z M32,9.071 L42.214,13 L32,16.929 L21.786,13 L32,9.071 Z M42.58,28.842 C40.694,29.686 37.528,30.669 33,30.801 L33,18.687 L44,14.457 L44,26.648 C44,27.599 43.444,28.46 42.58,28.842 L42.58,28.842 Z M20,26.648 L20,14.457 L31,18.687 L31,30.801 C26.472,30.669 23.306,29.686 21.416,28.84 C20.556,28.46 20,27.599 20,26.648 L20,26.648 Z M20.604,30.667 C22.805,31.652 26.569,32.827 32,32.827 C37.431,32.827 41.195,31.652 43.393,30.669 C44.977,29.968 46,28.39 46,26.648 L46,13 C46,12.586 45.745,12.215 45.359,12.066 L32.359,7.066 C32.127,6.978 31.873,6.978 31.641,7.066 L18.641,12.066 C18.255,12.215 18,12.586 18,13 L18,26.648 C18,28.39 19.023,29.968 20.604,30.667 L20.604,30.667 Z" id="AWS-Fargate_Icon_64_Squid"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,23 @@
|
||||
<svg width="150" height="150" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="e399c19f-b68f-429d-b176-18c2117ff73c" x1="-1032.172" x2="-1059.213" y1="145.312" y2="65.426" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#114a8b"/>
|
||||
<stop offset="1" stop-color="#0669bc"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15" x1="-1023.725" x2="-1029.98" y1="108.083" y2="105.968" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-opacity=".3"/>
|
||||
<stop offset=".071" stop-opacity=".2"/>
|
||||
<stop offset=".321" stop-opacity=".1"/>
|
||||
<stop offset=".623" stop-opacity=".05"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="a7fee970-a784-4bb1-af8d-63d18e5f7db9" x1="-1027.165" x2="-997.482" y1="147.642" y2="68.561" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#3ccbf4"/>
|
||||
<stop offset="1" stop-color="#2892df"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)" d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"/>
|
||||
<path fill="#0078d4" d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"/>
|
||||
<path fill="url(#ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15)" d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"/>
|
||||
<path fill="url(#a7fee970-a784-4bb1-af8d-63d18e5f7db9)" d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 97.75 78.62"><path d="M62.21 21.73l3.13.06 8.53-8.51.42-3.6a38.2 38.2 0 0 0-62.3 18.57c.88-.64 2.88-.16 2.88-.16l17-2.8s.88-1.44 1.31-1.35a21.19 21.19 0 0 1 29-2.21z" fill="#ea4335"/><path d="M85.79 28.27a38.32 38.32 0 0 0-11.54-18.6l-12 12a21.2 21.2 0 0 1 7.92 16.53v2.19a10.63 10.63 0 1 1 0 21.25h-21.3l-2.12 2.14v12.73l2.12 2.12h21.25a27.61 27.61 0 0 0 15.67-50.36z" fill="#4285f4"/><path d="M27.62 78.63h21.25v-17H27.62a10.54 10.54 0 0 1-4.38-1l-3.06.94-8.51 8.55-.74 2.88a27.47 27.47 0 0 0 16.69 5.63z" fill="#34a853"/><path d="M27.62 23.39A27.61 27.61 0 0 0 10.94 73l12.32-12.32a10.62 10.62 0 1 1 14-14l12.36-12.36a27.6 27.6 0 0 0-22-10.93z" fill="#fbbc04"/></svg>
|
||||
|
After Width: | Height: | Size: 723 B |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,65 @@
|
||||
[tool.poetry]
|
||||
name = "llm-app"
|
||||
version = "0.3.6"
|
||||
description = "LLM-App is a library for creating responsive AI applications leveraging OpenAI/Hugging Face APIs to provide responses to user queries based on live data sources. Build your own LLM application in 30 lines of code, no vector database required."
|
||||
authors = [
|
||||
"Jan Chorowski <janek@pathway.com>",
|
||||
"Kamil Piechowiak <kamil@pathway.com>",
|
||||
"Jakub Kowalski <kuba@pathway.com>",
|
||||
"Michał Bartoszkiewicz <embe@pathway.com>",
|
||||
"Mohamed Malhou <mohamed@pathway.com>",
|
||||
"Sergey Kulik <sergey@pathway.com>",
|
||||
"Adrian Kosowski <adrian@pathway.com>",
|
||||
"Mateusz Lewandowski <mateusz@pathway.com>",
|
||||
"Olivier Ruas <olivier@pathway.com>",
|
||||
]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
keywords = ["Pathway", "LLM"]
|
||||
package-mode = false
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
]
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Homepage" = "https://github.com/pathwaycom/llm-app"
|
||||
"Source Code" = "https://github.com/pathwaycom/llm-app"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<3.13"
|
||||
pathway = "^0.12.0"
|
||||
|
||||
[tool.poetry.group.linters]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.linters.dependencies]
|
||||
black = "^24.2.0"
|
||||
isort = "^5.13.2"
|
||||
mypy = "~1.10.0"
|
||||
flake8 = "~7.0.0"
|
||||
pytest = "^8.0.2"
|
||||
types-requests = "^2.31.0"
|
||||
types-PyYAML = "^6.0.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.black]
|
||||
target-version = ["py310", "py311"]
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.10"
|
||||
exclude = ["templates/adaptive_rag", "templates/private_rag", "templates/document_indexing", "templates/multimodal_rag", "templates/question_answering_rag", "templates/video_rag_twelvelabs"]
|
||||
ignore_missing_imports = true
|
||||
check_untyped_defs = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
strict_equality = true
|
||||
@@ -0,0 +1,9 @@
|
||||
[flake8]
|
||||
exclude =
|
||||
.git
|
||||
|
||||
max-line-length = 119
|
||||
docstring-convention = google
|
||||
extend-ignore =
|
||||
# Black (https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8)
|
||||
E203, E701
|
||||
@@ -0,0 +1 @@
|
||||
OPENAI_API_KEY="sk-***"
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,168 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
|
||||
# End to end Adaptive RAG with Pathway Live Data Framework
|
||||
|
||||
This is the accompanying code for deploying the `adaptive RAG` technique with Pathway. To understand the technique and learn how it can save tokens without sacrificing accuracy, read [our showcase](https://pathway.com/developers/templates/rag/adaptive-rag).
|
||||
|
||||
To learn more about building & deploying RAG applications with Pathway Live Data Framework, including containerization, refer to [demo question answering](../question_answering_rag/README.md).
|
||||
|
||||
## Introduction
|
||||
This app relies on modules provided under `pathway.xpacks.llm`.
|
||||
|
||||
`BaseRAGQuestionAnswerer` is the base class to build RAG applications with Pathway Live Data Framework vector store and Pathway Live Data Framework xpack components.
|
||||
It is meant to get you started with your RAG application right away.
|
||||
|
||||
Here, we extend the `BaseRAGQuestionAnswerer` to implement the adaptive retrieval and reply to requests in the endpoint `/v2/answer`.
|
||||
Since we are interested in changing the behavior and logic of the RAG, we only modify `answer` function that handles all this logic, and then replies to the post request.
|
||||
|
||||
`answer` function takes the `pw_ai_queries` table as the input, this table contains the prompt, and other arguments coming from the post request, see the `BaseRAGQuestionAnswerer` class and defined schemas to learn more about getting inputs with post requests.
|
||||
We use the data in this table to call our adaptive retrieval logic.
|
||||
|
||||
To do that, we use `answer_with_geometric_rag_strategy_from_index` implementation provided under the `pathway.xpacks.llm.question_answering`.
|
||||
This function takes an index, LLM, prompt and adaptive parameters such as the starting number of documents. Then, iteratively asks the question to the LLM with an increasing number of context documents retrieved from the index.
|
||||
|
||||
We encourage you to check the implementation of `answer_with_geometric_rag_strategy_from_index`.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway templates, read [our guide](https://pathway.com/developers/templates/configure-yaml).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- LLM
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
Pathway [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
You can also check our other templates - [Question Answering RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag),
|
||||
[Multimodal RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/multimodal_rag) or
|
||||
[Private RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/private_rag). As all of these only differ
|
||||
in the YAML configuration file, you can also use them as an inspiration for your custom pipeline.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### LLM Model
|
||||
|
||||
You can choose any of the models offered by Open AI, like GPT-5, GPT-4.1, or GPT-4o.
|
||||
You can find the whole list on their [models page](https://platform.openai.com/docs/models).
|
||||
|
||||
You simply need to change the `model` to the one you want to use, e.g., to use GPT-5:
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-5"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache
|
||||
capacity: 8
|
||||
```
|
||||
|
||||
The default model is `gpt-4.1-mini`.
|
||||
|
||||
You can also use different provider, by using different class from [Pathway Live Data Framework LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview),
|
||||
e.g. here is configuration for locally run Mistral model.
|
||||
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.LiteLLMChat
|
||||
model: "ollama/mistral"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DiskCache
|
||||
temperature: 0
|
||||
top_p: 1
|
||||
api_base: "http://localhost:11434"
|
||||
```
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the host and the port of the webserver.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache or persistence, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
persistence_mode: !pw.PersistenceMode.UDF_CACHING
|
||||
persistence_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `data` folder.
|
||||
|
||||
#### Local Data Source
|
||||
|
||||
The local data source is configured by using map with tag `!pw.io.fs.read`. Then set `path` to denote the path to a folder with files to be indexed.
|
||||
|
||||
#### Google Drive Data Source
|
||||
|
||||
The Google Drive data source is enabled by using map with tag `!pw.io.gdrive.read`. The map must contain two main parameters:
|
||||
- `object_id`, containing the ID of the folder that needs to be indexed. It can be found from the URL in the web interface, where it's the last part of the address. For example, the publicly available demo folder in Google Drive has the URL `https://drive.google.com/drive/folders/1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`. Consequently, the last part of this address is `1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`, hence this is the `object_id` you would need to specify.
|
||||
- `service_user_credentials_file`, containing the path to the credentials files for the Google [service account](https://cloud.google.com/iam/docs/service-account-overview). To get more details on setting up the service account and getting credentials, you can also refer to [this tutorial](https://pathway.com/developers/user-guide/connectors/gdrive-connector#setting-up-google-drive).
|
||||
|
||||
Besides, to speed up the indexing process you may want to specify the `refresh_interval` parameter, denoted by an integer number of seconds. It corresponds to the frequency between two sequential folder scans. If unset, it defaults to 30 seconds.
|
||||
|
||||
For the full list of the available parameters, please refer to the Google Drive connector [documentation](https://pathway.com/developers/api-docs/pathway-io/gdrive#pathway.io.gdrive.read).
|
||||
|
||||
#### SharePoint Data Source
|
||||
|
||||
This data source requires Scale or Enterprise [license key](https://pathway.com/pricing) - you can obtain free Scale key on [Pathway website](https://pathway.com/get-license).
|
||||
|
||||
To use it, set the map tag to be `!pw.xpacks.connectors.sharepoint.read`, and then provide values of `url`, `tenant`, `client_id`, `cert_path`, `thumbprint` and `root_path`. To read about the meaning of these arguments, check the Sharepoint connector [documentation](https://pathway.com/developers/api-docs/pathway-xpacks-sharepoint#pathway.xpacks.connectors.sharepoint.read).
|
||||
|
||||
## Running the app
|
||||
To run the app, depending on the configuration, you may need to set up environmntal variables with LLM provider keys. By default, this template uses OpenAI API, so to run it you need to set `OPENAI_API_KEY` environmental key or create an `.env` file in this directory with your key: `OPENAI_API_KEY=sk-...`. If you modify the code to use another LLM provider, you may need to set a relevant API key.
|
||||
|
||||
### With Docker
|
||||
In order to let the pipeline get updated with each change in local files, you need to mount the folder onto the docker. The following commands show how to do that.
|
||||
|
||||
```bash
|
||||
# Build the image in this folder
|
||||
docker build -t adaptiverag .
|
||||
|
||||
# Run the image, mount the `data` folder into image
|
||||
# -e is used to pass value of OPENAI_API_KEY environmental variable
|
||||
docker run -v ./data:/app/data -e OPENAI_API_KEY -p 8000:8000 adaptiverag
|
||||
```
|
||||
|
||||
### Locally
|
||||
To run locally you need to install the Pathway Live Data Framework app with LLM dependencies using:
|
||||
```bash
|
||||
pip install pathway[all]
|
||||
```
|
||||
|
||||
Then change your directory in the terminal to this folder and run the app:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
## Using the app
|
||||
|
||||
Finally, query the application with;
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "What is the start date of the contract?"
|
||||
}'
|
||||
```
|
||||
> `{"response": "December 21, 2015 [6]"}`
|
||||
@@ -0,0 +1,76 @@
|
||||
import logging
|
||||
from warnings import warn
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.question_answering import SummaryQuestionAnswerer
|
||||
from pathway.xpacks.llm.servers import QASummaryRestServer
|
||||
from pydantic import BaseModel, ConfigDict, InstanceOf
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
question_answerer: InstanceOf[SummaryQuestionAnswerer]
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
with_cache: bool | None = None # deprecated
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
terminate_on_error: bool = False
|
||||
|
||||
def run(self) -> None:
|
||||
server = QASummaryRestServer( # noqa: F841
|
||||
self.host, self.port, self.question_answerer
|
||||
)
|
||||
|
||||
if self.persistence_mode is None:
|
||||
if self.with_cache is True:
|
||||
warn(
|
||||
"`with_cache` is deprecated. Please use `persistence_mode` instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
persistence_mode = pw.PersistenceMode.UDF_CACHING
|
||||
else:
|
||||
persistence_mode = None
|
||||
else:
|
||||
persistence_mode = self.persistence_mode
|
||||
|
||||
if persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
|
||||
pw.run(
|
||||
persistence_config=persistence_config,
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,114 @@
|
||||
# This YAML configuration file is used to set up and configure the Adaptive RAG template.
|
||||
# It defines various components such as data sources, language models, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for generating responses.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: data
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
|
||||
|
||||
# Configures the LLM model settings for generating responses.
|
||||
# The list of available Pathway LLM wrappers is available here:
|
||||
# https://pathway.com/developers/api-docs/pathway-xpacks-llm/llms
|
||||
# You can learn more about those in our documentation:
|
||||
# https://pathway.com/developers/templates/rag-customization/llm-chats
|
||||
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-4.1-mini"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
capacity: 8
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.OpenAIEmbedder
|
||||
model: "text-embedding-3-small"
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy {}
|
||||
|
||||
# Defines the splitter settings for dividing text into smaller chunks.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Configures the question-answering component using the RAG approach.
|
||||
# The component builds a RAG over an index.
|
||||
# You can interact with obtained RAG using a REST API.
|
||||
# You can learn more about the available operations here:
|
||||
# https://pathway.com/developers/templates/rag-customization/rest-api
|
||||
question_answerer: !pw.xpacks.llm.question_answering.AdaptiveRAGQuestionAnswerer
|
||||
llm: $llm
|
||||
indexer: $document_store
|
||||
n_starting_documents: 2
|
||||
factor: 2
|
||||
max_iterations: 4
|
||||
|
||||
# Change host and port of the webserver by uncommenting these lines
|
||||
# host: "0.0.0.0"
|
||||
# port: 8000
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
@@ -0,0 +1 @@
|
||||
python-dotenv~=1.0
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,199 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
# Realtime Document Indexing: Vector Store with always-up-to-date knowledge
|
||||
|
||||
This is a basic service for a real-time document indexing pipeline powered by [Pathway Live Data Framework](https://github.com/pathwaycom/pathway).
|
||||
|
||||
The capabilities of the pipeline include:
|
||||
|
||||
- Real-time document indexing from Microsoft 365 SharePoint, Google Drive, or a local directory;
|
||||
- Similarity search by user query;
|
||||
- Filtering by the metadata according to the condition given in [JMESPath format](https://jmespath.org/);
|
||||
- Basic stats on the indexer's health.
|
||||
|
||||
## Summary of the Pipeline
|
||||
|
||||
This example spawns a lightweight webserver that accepts queries on three possible endpoints:
|
||||
- `/v1/retrieve` to perform similarity search;
|
||||
- `/v1/statistics` to get the basic stats about the indexer's health;
|
||||
- `/v1/inputs` to retrieve the metadata of all files currently processed by the indexer.
|
||||
|
||||
Please refer to the Open API doc on Hosted Pipelines [website](https://pathway.com/solutions/ai-pipelines) for the format of the requests to the endpoints.
|
||||
|
||||
## How It Works
|
||||
|
||||
This pipeline uses several Pathway Live Data Framework connectors to read the data from the local drive, Google Drive, and Microsoft SharePoint sources. It allows you to poll the changes with low latency and to do the modifications tracking. So, if something changes in the tracked files, the corresponding change is reflected in the internal collections. The contents are read into a single Pathway Live Data Framework Table as binary objects.
|
||||
|
||||
After that, those binary objects are parsed with [unstructured](https://unstructured.io/) library and split into chunks. With the usage of OpenAI API, the pipeline embeds the obtained chunks.
|
||||
|
||||
Finally, the embeddings are indexed with the capabilities of Pathway Live Data Framework's machine-learning library. The user can then query the created index with simple HTTP requests to the endpoints mentioned above.
|
||||
|
||||
## Pipeline Organization
|
||||
|
||||
This folder contains several objects:
|
||||
- `app.py`, the pipeline code using Pathway Live Data Framework and written in Python;
|
||||
- `app.yaml`, the file containing configuration of the pipeline, like embedding model, sources, or the server address;
|
||||
- `requirements.txt`, the textfile denoting the pip dependencies for running this pipeline. It can be passed to `pip install -r ...` to install everything that is needed to launch the pipeline locally;
|
||||
- `Dockerfile`, the Docker configuration for running the pipeline in the container;
|
||||
- `docker-compose.yml`, the docker-compose configuration for running the pipeline along with the chat UI;
|
||||
- `.env`, a short environment variables configuration file where the OpenAI key must be stored;
|
||||
- `files-for-indexing/`, a folder with exemplary files that can be used for the test runs.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway Live Data Framework templates, read [our guide](https://pathway.com/developers/templates/configure-yaml).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
The Pathway Live Data Framework [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### Embedding Model
|
||||
|
||||
By default this template uses locally run model `mixedbread-ai/mxbai-embed-large-v1`. If you wish, you can replace this with any other model, by changing
|
||||
`$embedder` in `app.yaml`. For example, to use OpenAI embedder, set:
|
||||
```yaml
|
||||
$embedder: !pw.xpacks.llm.embedders.OpenAIEmbedder
|
||||
model: "text-embedding-ada-002"
|
||||
cache_strategy: !pw.udfs.DiskCache
|
||||
```
|
||||
|
||||
If you choose to use a provider, that requires API key, remember to set appropriate environmental values (you can also set them in `.env` file).
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the host and the port of the webserver.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
with_cache: True
|
||||
cache_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway Live Data Framework package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `files-from-indexing` folder.
|
||||
|
||||
#### Local Data Source
|
||||
|
||||
The local data source is configured by using map with tag `!pw.io.fs.read`. Then set `path` to denote the path to a folder with files to be indexed.
|
||||
|
||||
#### Google Drive Data Source
|
||||
|
||||
The Google Drive data source is enabled by using map with tag `!pw.io.gdrive.read`. The map must contain two main parameters:
|
||||
- `object_id`, containing the ID of the folder that needs to be indexed. It can be found from the URL in the web interface, where it's the last part of the address. For example, the publicly available demo folder in Google Drive has the URL `https://drive.google.com/drive/folders/1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`. Consequently, the last part of this address is `1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`, hence this is the `object_id` you would need to specify.
|
||||
- `service_user_credentials_file`, containing the path to the credentials files for the Google [service account](https://cloud.google.com/iam/docs/service-account-overview). To get more details on setting up the service account and getting credentials, you can also refer to [this tutorial](https://pathway.com/developers/user-guide/connectors/gdrive-connector#setting-up-google-drive).
|
||||
|
||||
Besides, to speed up the indexing process you may want to specify the `refresh_interval` parameter, denoted by an integer number of seconds. It corresponds to the frequency between two sequential folder scans. If unset, it defaults to 30 seconds.
|
||||
|
||||
For the full list of the available parameters, please refer to the Google Drive connector [documentation](https://pathway.com/developers/api-docs/pathway-io/gdrive#pathway.io.gdrive.read).
|
||||
|
||||
#### SharePoint Data Source
|
||||
|
||||
This data source requires Scale or Enterprise [license key](https://pathway.com/pricing) - you can obtain free Scale key on [Pathway website](https://pathway.com/get-license).
|
||||
|
||||
To use it, set the map tag to be `!pw.xpacks.connectors.sharepoint.read`, and then provide values of `url`, `tenant`, `client_id`, `cert_path`, `thumbprint` and `root_path`. To read about the meaning of these arguments, check the Sharepoint connector [documentation](https://pathway.com/developers/api-docs/pathway-xpacks-sharepoint#pathway.xpacks.connectors.sharepoint.read).
|
||||
|
||||
## Running the Example
|
||||
|
||||
### Locally
|
||||
|
||||
This example can be run locally by executing `python main.py` in this directory. It has several command-line arguments:
|
||||
- `--host` denoting the host, where the server will run. The default setting is `0.0.0.0`;
|
||||
- `--port` denoting the port, where the server will accept requests. The default setting is `8000`;
|
||||
- `--sources-config` pointing to a data source configuration file, `sources_configuration.yaml` by default. You can customize it to change the folders indexed by the vector store. The free version supports `local` and `gdrive` hosted files, while the commercial one also supports `sharepoint` hosted folders. By default, the `local` option indexes files from the `files-for-indexing/` folder that is prefilled with exemplary documents.
|
||||
|
||||
Please note that the local run requires the dependencies to be installed. It can be done with a simple pip command:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### With Docker
|
||||
|
||||
First, create or fill the `.env` file in this folder (`/document_indexing_rag`) with your OpenAI key `OPENAI_API_KEY=sk-*******`.
|
||||
|
||||
To run jointly the vector indexing pipeline and a simple UI please execute:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Then, the UI will run at http://127.0.0.1:8501 by default. You can access it by following this URL in your web browser.
|
||||
|
||||
The `docker-compose.yml` file declares a [volume bind mount](https://docs.docker.com/reference/cli/docker/container/run/#volume) that makes changes to files under `files-for-indexing/` made on your host computer visible inside the docker container. If the index does not react to file changes, please check that the bind mount works
|
||||
by running `docker compose exec pathway_vector_indexer ls -l /app/files-for-indexing/` and verifying that all files are visible.
|
||||
|
||||
Alternatively, you can launch just the indexing pipeline as a single Docker container:
|
||||
|
||||
```bash
|
||||
docker build -t vector_indexer .
|
||||
docker run -v `pwd`/files-for-indexing:/app/files-for-indexing -p 8000:8000 vector_indexer
|
||||
```
|
||||
|
||||
The volume overlay is important - without it, docker will not see changes to files under the `files-for-indexing` folder.
|
||||
|
||||
## Querying the Example
|
||||
|
||||
Since the pipeline spawns an HTTP server and interacts with the user using the REST protocol, you can test it locally by sending requests with `curl`. To get the target address for the request, you can refer to the routes in the first section: `v1/retrieve`, `v1/statistics`, and `v1/inputs` and the Open API docs available at the Hosted Pipelines webpage.
|
||||
|
||||
Let's assume that the server runs with the default settings and therefore uses port 8000. That being said, you can query the similarity search endpoint with the following command:
|
||||
|
||||
```bash
|
||||
curl -X 'GET' \
|
||||
'http://localhost:8000/v1/retrieve?query=Pathway%20data%20processing%20framework&k=2' \
|
||||
-H 'accept: */*'
|
||||
```
|
||||
|
||||
Above, there are two CGI parameters to be customized: the `query`, which should contain the [urlencoded](https://en.wikipedia.org/wiki/Percent-encoding) search terms, and an integer `k` denoting the number of documents you want to retrieve.
|
||||
|
||||
Apart from the querying part, there is a health-check endpoint that shows the number of documents currently indexed and the most recent indexing time. It can be accessed, in a similar way, with the following command:
|
||||
|
||||
```bash
|
||||
curl -X 'GET' \
|
||||
'http://localhost:8000/v1/statistics' \
|
||||
-H 'accept: */*'
|
||||
```
|
||||
|
||||
Finally, there is a way to see the metadata of all documents that are currently indexed. To do that, you need to query the `v1/inputs` endpoint. The request command then looks as follows:
|
||||
|
||||
```bash
|
||||
curl -X 'GET' \
|
||||
'http://localhost:8000/v1/inputs' \
|
||||
-H 'accept: */*'
|
||||
```
|
||||
|
||||
Please make sure that you've changed the port in the requests if you did that when launching the main script.
|
||||
|
||||
## Adding Files to Index
|
||||
|
||||
To test index updates, simply add more files to the `files-for-indexing` folder if the local data source is used.
|
||||
If you are using Google Drive, simply upload your files in the folder configured in the `sources_configuration.yaml` file.
|
||||
|
||||
Then you can use the similarity search and stats endpoints, provided below.
|
||||
@@ -0,0 +1,75 @@
|
||||
import logging
|
||||
from warnings import warn
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.document_store import DocumentStore
|
||||
from pathway.xpacks.llm.servers import DocumentStoreServer
|
||||
from pydantic import BaseModel, ConfigDict, InstanceOf
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
document_store: InstanceOf[DocumentStore]
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
with_cache: bool | None = None # deprecated
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
terminate_on_error: bool = False
|
||||
|
||||
def run(self) -> None:
|
||||
server = DocumentStoreServer( # noqa: F841
|
||||
self.host, self.port, self.document_store
|
||||
)
|
||||
if self.persistence_mode is None:
|
||||
if self.with_cache is True:
|
||||
warn(
|
||||
"`with_cache` is deprecated. Please use `persistence_mode` instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
persistence_mode = pw.PersistenceMode.UDF_CACHING
|
||||
else:
|
||||
persistence_mode = None
|
||||
else:
|
||||
persistence_mode = self.persistence_mode
|
||||
|
||||
if persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
|
||||
pw.run(
|
||||
persistence_config=persistence_config,
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,88 @@
|
||||
# This YAML configuration file is used to set up and configure the Document indexing RAG template.
|
||||
# It defines various components such as data sources, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for answering the queries.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: files-for-indexing
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Model used for embedding
|
||||
$embedding_model: "mixedbread-ai/mxbai-embed-large-v1"
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.SentenceTransformerEmbedder
|
||||
model: $embedding_model
|
||||
call_kwargs:
|
||||
show_progress_bar: False
|
||||
|
||||
# Defines the splitter settings for dividing text into smaller chunks.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Change host and port of the webserver by uncommenting these lines
|
||||
# host: "0.0.0.0"
|
||||
# port: 8000
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
@@ -0,0 +1,22 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
pathway_vector_indexer:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
OPENAI_API_KEY: "${OPENAI_API_KEY}"
|
||||
volumes:
|
||||
- "./files-for-indexing:/app/files-for-indexing"
|
||||
streamlit_ui:
|
||||
depends_on:
|
||||
- pathway_vector_indexer
|
||||
build:
|
||||
context: https://github.com/pathway-labs/realtime-indexer-qa-chat.git
|
||||
ports:
|
||||
- "8501:8501"
|
||||
environment:
|
||||
OPENAI_API_KEY: "${OPENAI_API_KEY}"
|
||||
PATHWAY_HOST: "pathway_vector_indexer"
|
||||
PATHWAY_PORT: "8000"
|
||||
@@ -0,0 +1 @@
|
||||
python-dotenv~=1.0
|
||||
@@ -0,0 +1 @@
|
||||
PATHWAY_LICENSE_KEY="YOUR PATHWAY KEY" # can be obtained here: https://pathway.com/user/license
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8068
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,197 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
# MCP Server with Realtime Document Indexing
|
||||
|
||||
This is a template for exposing a real-time document indexing pipeline powered by [Pathway](https://github.com/pathwaycom/pathway) as an Model Context Protocol (MCP) server.
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is designed to standardize the way applications interact with large language models (LLMs). It serves as a bridge, much like a universal connector, enabling seamless integration between AI models and various data sources and tools. This protocol facilitates the creation of sophisticated AI workflows and agents, enhancing the capabilities of LLMs by connecting them with real-world data and functionalities.
|
||||
|
||||
The capabilities of the pipeline include:
|
||||
|
||||
- Real-time document indexing from Microsoft 365 SharePoint, Google Drive, or a local directory;
|
||||
- Similarity search by user query;
|
||||
- Filtering by the metadata according to the condition given in [JMESPath format](https://jmespath.org/);
|
||||
- The documents are available from a standardized MCP server.
|
||||
|
||||
## Summary of the Pipeline
|
||||
|
||||
This example spawns an MCP server that has three tools:
|
||||
- `retrieve_query` to perform similarity search on the indexed documents,
|
||||
- `statistics_query` to get the basic stats about the indexer's health,
|
||||
- `inputs_query` to retrieve the metadata of all files currently processed by the indexer.
|
||||
|
||||
You can get specification of those tools by querying the `list_tools` on the MCP server.
|
||||
|
||||
## How It Works
|
||||
|
||||
This pipeline uses several Pathway Live Data Framework connectors to read the data from the local drive, Google Drive, or Microsoft SharePoint sources. It allows you to poll the changes with low latency and to do the modifications tracking. So, if something changes in the tracked files, the corresponding change is reflected in the internal collections. The contents are read into a single Pathway Live Data Framework Table as binary objects.
|
||||
|
||||
After that, those binary objects are parsed with the [Docling](https://www.docling.ai/) library and split into chunks. With the usage of the [SentenceTransformer](https://www.sbert.net/) embedder, the pipeline embeds the obtained chunks.
|
||||
|
||||
Finally, the embeddings are indexed with the capabilities of Pathway Live Data Framework's machine-learning library. The user can then query the created index by connecting to the MCP server using an MCP client.
|
||||
|
||||
## Pipeline Organization
|
||||
|
||||
This folder contains several objects:
|
||||
- `app.py`, the pipeline code using Pathway Live Data Framework and written in Python;
|
||||
- `app.yaml`, the file containing configuration of the pipeline, like embedding model, sources, or the server address;
|
||||
- `requirements.txt`, the textfile denoting the pip dependencies for running this pipeline. It can be passed to `pip install -r requirements.txt` to install everything that is needed to launch the pipeline locally;
|
||||
- `Dockerfile`, the Docker configuration for running the pipeline in the container;
|
||||
- `docker-compose.yml`, the docker-compose configuration for running the pipeline along with the chat UI;
|
||||
- `files-for-indexing/`, a folder with exemplary files that can be used for the test runs.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway Live Data Framework templates, read [our guide](https://pathway.com/developers/templates/configure-yaml).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
The Pathway Live Data Framework [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### Embedding Model
|
||||
|
||||
By default this template uses locally run model `mixedbread-ai/mxbai-embed-large-v1`. If you wish, you can replace this with any other model, by changing
|
||||
`$embedder` in `app.yaml`. For example, to use OpenAI embedder, set:
|
||||
```yaml
|
||||
$embedder: !pw.xpacks.llm.embedders.OpenAIEmbedder
|
||||
model: "text-embedding-3-small"
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy {}
|
||||
```
|
||||
|
||||
If you choose to use a provider, that requires API key, remember to set appropriate environmental values (you can also set them in the `.env` file) - e.g. for using OpenAI embedders, set the `OPENAI_API_KEY` variable.
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the name, the host and the port of the MCP server.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
mcp_http: !pw.xpacks.llm.mcp_server.PathwayMcp
|
||||
name: "Streamable MCP Server"
|
||||
transport: "streamable-http"
|
||||
host: "localhost"
|
||||
port: 8068
|
||||
serve:
|
||||
- $document_store
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache or persistence, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
persistence_mode: !pw.PersistenceMode.UDF_CACHING
|
||||
persistence_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `files-from-indexing` folder.
|
||||
|
||||
#### Local Data Source
|
||||
|
||||
The local data source is configured by using map with tag `!pw.io.fs.read`. Then set `path` to denote the path to a folder with files to be indexed.
|
||||
|
||||
#### Google Drive Data Source
|
||||
|
||||
The Google Drive data source is enabled by using map with tag `!pw.io.gdrive.read`. The map must contain two main parameters:
|
||||
- `object_id`, containing the ID of the folder that needs to be indexed. It can be found from the URL in the web interface, where it's the last part of the address. For example, the publicly available demo folder in Google Drive has the URL `https://drive.google.com/drive/folders/1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`. Consequently, the last part of this address is `1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`, hence this is the `object_id` you would need to specify.
|
||||
- `service_user_credentials_file`, containing the path to the credentials files for the Google [service account](https://cloud.google.com/iam/docs/service-account-overview). To get more details on setting up the service account and getting credentials, you can also refer to [this tutorial](https://pathway.com/developers/user-guide/connectors/gdrive-connector#setting-up-google-drive).
|
||||
|
||||
Besides, to speed up the indexing process you may want to specify the `refresh_interval` parameter, denoted by an integer number of seconds. It corresponds to the frequency between two sequential folder scans. If unset, it defaults to 30 seconds.
|
||||
|
||||
For the full list of the available parameters, please refer to the Google Drive connector [documentation](https://pathway.com/developers/api-docs/pathway-io/gdrive#pathway.io.gdrive.read).
|
||||
|
||||
#### SharePoint Data Source
|
||||
|
||||
This data source requires Scale or Enterprise [license key](https://pathway.com/pricing) - you can obtain free Scale key on [Pathway website](https://pathway.com/get-license).
|
||||
|
||||
To use it, set the map tag to be `!pw.xpacks.connectors.sharepoint.read`, and then provide values of `url`, `tenant`, `client_id`, `cert_path`, `thumbprint` and `root_path`. To read about the meaning of these arguments, check the Sharepoint connector [documentation](https://pathway.com/developers/api-docs/pathway-xpacks-sharepoint#pathway.xpacks.connectors.sharepoint.read).
|
||||
|
||||
## Running the Template
|
||||
|
||||
### Pathway Live Data Framework License Key
|
||||
The Pathway Live Data Framework MCP Server requires a Pathway Live Data Framework license key, so before you run the template, you need to set the license key. This template is available for free via [Pathway Live Data Framework Scale](https://pathway.com/features), for which you can get the license key [here](https://pathway.com/user/license). Once you have your license key, create a `.env` file, in which set `PATHWAY_LICENSE_KEY` to your license key - see `.env.example` for an example of `.env` file.
|
||||
|
||||
### Locally
|
||||
|
||||
This template can be run locally by executing `python app.py` in this directory. Please note that the local run requires the `Pathway` library and other dependencies to be installed. It can be done with a pip command:
|
||||
|
||||
```bash
|
||||
pip install pathway[all]
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### With Docker`.
|
||||
|
||||
To run jointly the MCP server with real-time document indexint, please execute:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
The `docker-compose.yml` file declares a [volume bind mount](https://docs.docker.com/reference/cli/docker/container/run/#volume) that makes changes to files under `files-for-indexing/` made on your host computer visible inside the docker container. If the index does not react to file changes, please check that the bind mount works
|
||||
by running `docker compose exec pathway_vector_indexer ls -l /app/files-for-indexing/` and verifying that all files are visible.
|
||||
|
||||
|
||||
## Querying the Template with an MCP client
|
||||
|
||||
To test your examples, you need an MCP client which will connect to your MCP server. You can use the fastmcp package to define a client as follows:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from fastmcp import Client
|
||||
|
||||
# Change the URL if you change the default values in the app.yaml
|
||||
PATHWAY_MCP_URL = "http://localhost:8068/mcp/"
|
||||
|
||||
client = Client(PATHWAY_MCP_URL)
|
||||
|
||||
|
||||
async def main():
|
||||
async with client:
|
||||
tools = await client.list_tools()
|
||||
print(tools)
|
||||
|
||||
async with client:
|
||||
result = await client.call_tool(
|
||||
name="retrieve_query",
|
||||
arguments={"query": "How to create a webserver in Pathway?", "k": 3},
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
```
|
||||
|
||||
You can list the different tools available in the MCP server using the `list_tools` of the client. To access a given tool, you can use the method call_tool, with the name and arguments parameters. The arguments should be a dict of the different values: in this case, the `retrieve_query` tool has two required arguments: `query` and `k`.
|
||||
|
||||
## Using MCP server in Claude Desktop
|
||||
To use MCP server created by this template in Claude Desktop, follow the [guide in Pathway's documentation](https://pathway.com/developers/user-guide/llm-xpack/pathway-mcp-claude-desktop).
|
||||
|
||||
## Adding Files to Index
|
||||
|
||||
To test index updates, simply add more files to the `files-for-indexing` folder if the local data source is used.
|
||||
If you are using Google Drive, simply upload your files in the folder configured in the `sources_configuration.yaml` file.
|
||||
|
||||
Then you can use the similarity search and stats endpoints, provided below.
|
||||
@@ -0,0 +1,52 @@
|
||||
import logging
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.mcp_server import PathwayMcp
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
mcp_http: PathwayMcp
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
terminate_on_error: bool = False
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
|
||||
def run(self) -> None:
|
||||
if self.persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=self.persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
pw.run(
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
persistence_config=persistence_config,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
print(config)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,93 @@
|
||||
# This YAML configuration file is used to set up and configure the Document indexing RAG template.
|
||||
# It defines various components such as data sources, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for answering the queries.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: files-for-indexing
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Model used for embedding
|
||||
$embedding_model: "mixedbread-ai/mxbai-embed-large-v1"
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.SentenceTransformerEmbedder
|
||||
model: $embedding_model
|
||||
call_kwargs:
|
||||
show_progress_bar: False
|
||||
|
||||
# Defines the splitter settings for dividing text into smaller chunks.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Streamable MCP server, can be proxied
|
||||
mcp_http: !pw.xpacks.llm.mcp_server.PathwayMcp
|
||||
name: "Streamable MCP Server"
|
||||
transport: "streamable-http"
|
||||
host: "0.0.0.0"
|
||||
port: 8068
|
||||
serve:
|
||||
- $document_store
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
@@ -0,0 +1,11 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
pathway_mcp_server:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- "8068:8068"
|
||||
environment:
|
||||
PATHWAY_LICENSE_KEY: $PATHWAY_LICENSE_KEY
|
||||
volumes:
|
||||
- "./files-for-indexing:/app/files-for-indexing"
|
||||
@@ -0,0 +1 @@
|
||||
python-dotenv~=1.0
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,134 @@
|
||||
# Pathway Live Data Framework + LLM + Slack notification: RAG App with real-time alerting when answers change in documents
|
||||
|
||||
Microservice for a context-aware alerting ChatGPT assistant.
|
||||
|
||||
This demo is very similar to the `alert` example, the only difference is the data source (Google Drive)
|
||||
For the demo, alerts are sent to Slack (you need to provide `slack_alert_channel_id` and `slack_alert_token`),
|
||||
you can either put these env variables in .env file,
|
||||
or create env variables in the terminal (i.e. export in bash).
|
||||
|
||||
The program then starts a REST API endpoint serving queries about Google Docs stored in a
|
||||
Google Drive folder.
|
||||
|
||||
We can create notifications by asking from Streamlit or sending query to API stating we want to be notified.
|
||||
One example would be `Tell me and alert about the start date of the campaign for Magic Cola`
|
||||
|
||||
## How Does It Work?
|
||||
|
||||
First, Pathway Live Data Framework connects to Google Drive, extracts all documents, splits them into chunks, turns them into
|
||||
vectors using OpenAI embedding service, and store in a nearest neighbor index.
|
||||
|
||||
Each query text is first turned into a vector, then relevant document chunks are found
|
||||
using the nearest neighbor index. A prompt is built from the relevant chunk
|
||||
and sent to the OpenAI GPT3.5 chat service for processing and answering.
|
||||
|
||||
After an initial answer is provided, Pathway Live Data Framework monitors changes to documents and selectively
|
||||
re-triggers potentially affected queries. If the new answer is significantly different from
|
||||
the previously presented one, a new notification is created.
|
||||
|
||||
## How to run the project
|
||||
|
||||
Before running the app, you will need to give the app access to the Google Drive folder, we follow the steps below.
|
||||
|
||||
In order to access files on your Google Drive from the Pathway Live Data Framework app, you will need a Google Cloud project and a service user.
|
||||
|
||||
### Create a new project in the Google API console:
|
||||
|
||||
- Go to [https://console.cloud.google.com/projectcreate](https://console.cloud.google.com/projectcreate) and create new project
|
||||
- Enable Google Drive API by going to [https://console.cloud.google.com/apis/library/drive.googleapis.com](https://console.cloud.google.com/apis/library/drive.googleapis.com), make sure the newly created project is selected in the top left corner
|
||||
- Configure consent screen:
|
||||
- Go to [https://console.cloud.google.com/apis/credentials/consent](https://console.cloud.google.com/apis/credentials/consent)
|
||||
- If using a private Gmail, select "External", and go next.
|
||||
- Fill required parameters: application name, user support, and developer email (your email is fine)
|
||||
- On the next screen click "Add or remove scopes" search for "drive.readonly" and select this scope
|
||||
- Save and click through other steps
|
||||
- Create service user:
|
||||
|
||||
- Go to [https://console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials)
|
||||
- Click "+ Create credentials" and create a service account
|
||||
- Name the service user and click through the next steps
|
||||
- Generate service user key:
|
||||
- Once more go to [https://console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials) and click on your newly created user (under Service Accounts)
|
||||
- Go to "Keys", click "Add key" -> "Create new key" -> "JSON"
|
||||
|
||||
A JSON file will be saved to your computer.
|
||||
|
||||
Rename this JSON file to `secrets.json` and put it under `templates/drive_alert` next to `app.py` so that it is easily reachable from the app.
|
||||
|
||||
You can now share desired Google Drive resources with the created user.
|
||||
Note the email ending with `gserviceaccount.com` we will share the folder with this email.
|
||||
|
||||
Once you've done it, you will need an ID of some file or directory. You can obtain it manually by right-clicking on the file -> share -> copy link. It will be part of the URL.
|
||||
|
||||
[https://drive.google.com/file/d/[FILE_ID]/view?usp=drive_link](https://drive.google.com/file/d/%5BFILE_ID%5D/view?usp=drive_link)
|
||||
|
||||
For folders,
|
||||
First, right-click on the folder and click share, link will be of the format: [https://drive.google.com/drive/folders/[folder_id]?usp=drive_link](https://drive.google.com/drive/folders/%7Bfolder_id%7D?usp=drive_link)
|
||||
Copy the folder_id from the URL.
|
||||
Second, click on share and share the folder with the email ending with `gserviceaccount.com`
|
||||
|
||||
### Setup Slack notifications:
|
||||
|
||||
For this demo, Slack notifications are optional and notifications will be printed if no Slack API keys are provided. See: [Slack Apps](https://api.slack.com/apps) and [Getting a token](https://api.slack.com/tutorials/tracks/getting-a-token).
|
||||
Your Slack application will need at least `chat:write.public` scope enabled.
|
||||
|
||||
### Setup environment:
|
||||
Set your env variables in the .env file placed in this directory.
|
||||
|
||||
```bash
|
||||
OPENAI_API_KEY=sk-...
|
||||
SLACK_ALERT_CHANNEL_ID= # If unset, alerts will be printed to the terminal
|
||||
SLACK_ALERT_TOKEN=
|
||||
FILE_OR_DIRECTORY_ID= # file or folder ID that you want to track that we have retrieved earlier
|
||||
GOOGLE_CREDS=./secrets.json # Default location of Google Drive authorization secrets
|
||||
PATHWAY_PERSISTENT_STORAGE= # Set this variable if you want to use caching
|
||||
```
|
||||
|
||||
### Run with Docker
|
||||
|
||||
To run jointly the Alert pipeline and a simple UI execute:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Then, the UI will run at http://0.0.0.0:8501 by default. You can access it by following this URL in your web browser.
|
||||
|
||||
The `docker-compose.yml` file declares a [volume bind mount](https://docs.docker.com/reference/cli/docker/container/run/#volume) that makes changes to files under `data/` made on your host computer visible inside the docker container. The files in `data/live` are indexed by the pipeline - you can paste new files there and they will impact the computations.
|
||||
|
||||
### Run manually
|
||||
|
||||
Alternatively, you can run each service separately.
|
||||
|
||||
Make sure you have installed poetry dependencies with `--extras unstructured`.
|
||||
```bash
|
||||
poetry install --with examples --extras unstructured
|
||||
```
|
||||
|
||||
Then run:
|
||||
```bash
|
||||
poetry run python app.py
|
||||
```
|
||||
|
||||
If all dependencies are managed manually rather than using poetry, you can alternatively use:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
To run the Streamlit UI, run:
|
||||
```bash
|
||||
streamlit run ui/server.py --server.port 8501 --server.address 0.0.0.0
|
||||
```
|
||||
|
||||
### Querying the pipeline
|
||||
|
||||
To create alerts, you can call the REST API:
|
||||
|
||||
```bash
|
||||
curl --data '{
|
||||
"user": "user",
|
||||
"query": "When does the magic cola campaign start? Alert me if the start date changes."
|
||||
}' http://localhost:8080/ | jq
|
||||
```
|
||||
|
||||
or access the Streamlit UI at `0.0.0.0:8501`.
|
||||
@@ -0,0 +1,3 @@
|
||||
from .app import run
|
||||
|
||||
__all__ = ["run"]
|
||||
@@ -0,0 +1,301 @@
|
||||
"""
|
||||
Microservice for a context-aware alerting ChatGPT assistant.
|
||||
|
||||
This demo is very similar to the `alert` example, the only difference is the data source (Google Drive)
|
||||
For the demo, alerts are sent to Slack (you need to provide `slack_alert_channel_id` and `slack_alert_token`),
|
||||
you can either put these env variables in .env file under llm-app directory,
|
||||
or create env variables in the terminal (i.e. export in bash).
|
||||
|
||||
The program then starts a REST API endpoint serving queries about Google Docs stored in a
|
||||
Google Drive folder.
|
||||
|
||||
We can create notifications by asking from Streamlit or sending query to API stating we want to be notified.
|
||||
One example would be `Tell me and alert about the start date of the campaign for Magic Cola`
|
||||
|
||||
How Does It Work?
|
||||
First, Pathway connects to Google Drive, extracts all documents, splits them into chunks, turns them into
|
||||
vectors using OpenAI embedding service, and store in a nearest neighbor index.
|
||||
|
||||
Each query text is first turned into a vector, then relevant document chunks are found
|
||||
using the nearest neighbor index. A prompt is built from the relevant chunk
|
||||
and sent to the OpenAI GPT3.5 chat service for processing and answering.
|
||||
|
||||
After an initial answer is provided, Pathway monitors changes to documents and selectively
|
||||
re-triggers potentially affected queries. If the new answer is significantly different from
|
||||
the previously presented one, a new notification is created.
|
||||
|
||||
Please check the README.md in this directory for how-to-run instructions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import dotenv
|
||||
import pathway as pw
|
||||
from pathway.stdlib.ml.index import KNNIndex
|
||||
from pathway.xpacks.llm.embedders import OpenAIEmbedder
|
||||
from pathway.xpacks.llm.llms import OpenAIChat, prompt_chat_single_qa
|
||||
from pathway.xpacks.llm.parsers import UnstructuredParser
|
||||
from pathway.xpacks.llm.splitters import TokenCountSplitter
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
|
||||
class DocumentInputSchema(pw.Schema):
|
||||
doc: str
|
||||
|
||||
|
||||
class QueryInputSchema(pw.Schema):
|
||||
query: str
|
||||
user: str
|
||||
|
||||
|
||||
# Helper Functions
|
||||
@pw.udf
|
||||
def build_prompt(documents, query):
|
||||
docs_str = "\n".join(
|
||||
[f"Doc-({idx}) -> {doc}" for idx, doc in enumerate(documents[::-1])]
|
||||
)
|
||||
prompt = f"""Given a set of documents, answer user query. If answer is not in docs, say it cant be inferred.
|
||||
|
||||
Docs: {docs_str}
|
||||
Query: '{query}'
|
||||
Final Response:"""
|
||||
return prompt
|
||||
|
||||
|
||||
@pw.udf
|
||||
def build_prompt_check_for_alert_request_and_extract_query(query: str) -> str:
|
||||
prompt = f"""Evaluate the user's query and identify if there is a request for notifications on answer alterations:
|
||||
User Query: '{query}'
|
||||
|
||||
Respond with 'Yes' if there is a request for alerts, and 'No' if not,
|
||||
followed by the query without the alerting request part.
|
||||
|
||||
Examples:
|
||||
"Tell me about windows in Pathway" => "No. Tell me about windows in Pathway"
|
||||
"Tell me and alert about windows in Pathway" => "Yes. Tell me about windows in Pathway"
|
||||
"""
|
||||
return prompt
|
||||
|
||||
|
||||
@pw.udf
|
||||
def split_answer(answer: str) -> tuple[bool, str]:
|
||||
alert_enabled = "yes" in answer[:3].lower()
|
||||
true_query = answer[3:].strip(' ."')
|
||||
return alert_enabled, true_query
|
||||
|
||||
|
||||
def build_prompt_compare_answers(new: str, old: str) -> str:
|
||||
prompt = f"""
|
||||
Are the two following responses deviating?
|
||||
Answer with Yes or No.
|
||||
|
||||
First response: "{old}"
|
||||
|
||||
Second response: "{new}"
|
||||
"""
|
||||
return prompt
|
||||
|
||||
|
||||
def make_query_id(user, query) -> str:
|
||||
return str(hash(query + user))
|
||||
|
||||
|
||||
@pw.udf
|
||||
def construct_notification_message(query: str, response: str) -> str:
|
||||
return f'New response for question "{query}":\n{response}'
|
||||
|
||||
|
||||
@pw.udf
|
||||
def construct_message(response, alert_flag, metainfo=None):
|
||||
if alert_flag:
|
||||
if metainfo:
|
||||
response += "\n" + str(metainfo)
|
||||
return response + "\n\n🔔 Activated"
|
||||
return response
|
||||
|
||||
|
||||
def decision_to_bool(decision: str) -> bool:
|
||||
return "yes" in decision.lower()
|
||||
|
||||
|
||||
def run(
|
||||
*,
|
||||
object_id=os.environ.get("FILE_OR_DIRECTORY_ID", ""),
|
||||
api_key: str = os.environ.get("OPENAI_API_KEY", ""),
|
||||
host: str = os.environ.get("PATHWAY_REST_CONNECTOR_HOST", "0.0.0.0"),
|
||||
port: int = int(os.environ.get("PATHWAY_REST_CONNECTOR_PORT", "8080")),
|
||||
embedder_locator: str = "text-embedding-ada-002",
|
||||
embedding_dimension: int = 1536,
|
||||
model_locator: str = "gpt-3.5-turbo",
|
||||
max_tokens: int = 400,
|
||||
temperature: float = 0.0,
|
||||
slack_alert_channel_id=os.environ.get("SLACK_ALERT_CHANNEL_ID", ""),
|
||||
slack_alert_token=os.environ.get("SLACK_ALERT_TOKEN", ""),
|
||||
service_user_credentials_file=os.environ.get(
|
||||
"GOOGLE_CREDS", "templates/drive_alert/secrets.json"
|
||||
),
|
||||
**kwargs,
|
||||
):
|
||||
# Part I: Build index
|
||||
embedder = OpenAIEmbedder(
|
||||
api_key=api_key,
|
||||
model=embedder_locator,
|
||||
retry_strategy=pw.asynchronous.FixedDelayRetryStrategy(),
|
||||
cache_strategy=pw.asynchronous.DefaultCache(),
|
||||
)
|
||||
|
||||
# We start building the computational graph. Each pathway variable represents a
|
||||
# dynamically changing table.
|
||||
|
||||
# The files table contains contents of documents in Google Drive.
|
||||
# Pathway automatically tracks changes to files and propagates these changes through
|
||||
# following computations.
|
||||
# Other Pathway connectors can be used as well - notably:
|
||||
# - pw.io.fs.read to load and track changes to the local drive and
|
||||
# - pw.io.s3.read to use an S3-compatible storage
|
||||
files = pw.io.gdrive.read(
|
||||
object_id=object_id,
|
||||
service_user_credentials_file=service_user_credentials_file,
|
||||
refresh_interval=30, # interval between fetch operations in seconds, lower this for more responsiveness
|
||||
)
|
||||
parser = UnstructuredParser()
|
||||
documents = files.select(texts=parser(pw.this.data))
|
||||
documents = documents.flatten(pw.this.texts)
|
||||
documents = documents.select(texts=pw.this.texts[0])
|
||||
|
||||
splitter = TokenCountSplitter()
|
||||
documents = documents.select(
|
||||
chunks=splitter(pw.this.texts, min_tokens=40, max_tokens=120)
|
||||
)
|
||||
documents = documents.flatten(pw.this.chunks)
|
||||
documents = documents.select(chunk=pw.this.chunks[0])
|
||||
|
||||
enriched_documents = documents + documents.select(data=embedder(pw.this.chunk))
|
||||
|
||||
# The index is updated each time a file changes.
|
||||
index = KNNIndex(
|
||||
enriched_documents.data, enriched_documents, n_dimensions=embedding_dimension
|
||||
)
|
||||
|
||||
# Part II: receive queries, detect intent and prepare cleaned query
|
||||
|
||||
# The rest_connector returns a table of all queries under processing
|
||||
query, response_writer = pw.io.http.rest_connector(
|
||||
host=host,
|
||||
port=port,
|
||||
schema=QueryInputSchema,
|
||||
autocommit_duration_ms=50,
|
||||
delete_completed_queries=False,
|
||||
)
|
||||
|
||||
model = OpenAIChat(
|
||||
api_key=api_key,
|
||||
model=model_locator,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
retry_strategy=pw.asynchronous.FixedDelayRetryStrategy(),
|
||||
cache_strategy=pw.asynchronous.DefaultCache(),
|
||||
)
|
||||
|
||||
# Pre-process the queries:
|
||||
# - detect alerting intent
|
||||
# - then embed the query for nearest neighbor retrieval
|
||||
query += query.select(
|
||||
prompt=build_prompt_check_for_alert_request_and_extract_query(query.query)
|
||||
)
|
||||
query += query.select(
|
||||
tupled=split_answer(
|
||||
model(
|
||||
prompt_chat_single_qa(pw.this.prompt),
|
||||
max_tokens=100,
|
||||
)
|
||||
),
|
||||
)
|
||||
query = query.select(
|
||||
pw.this.user,
|
||||
alert_enabled=pw.this.tupled[0],
|
||||
query=pw.this.tupled[1],
|
||||
)
|
||||
|
||||
query += query.select(
|
||||
data=embedder(pw.this.query),
|
||||
query_id=pw.apply(make_query_id, pw.this.user, pw.this.query),
|
||||
)
|
||||
|
||||
# Part III: respond to queries
|
||||
|
||||
# The context is a dynamic table: Pathway updates it each time:
|
||||
# - a new query arrives
|
||||
# - a source document is changed significantly enough to change the set of
|
||||
# nearest neighbors
|
||||
query_context = query + index.get_nearest_items(query.data, k=3).select(
|
||||
documents_list=pw.this.chunk
|
||||
).with_universe_of(query)
|
||||
|
||||
# then we answer the queries using retrieved documents
|
||||
prompt = query_context.select(
|
||||
pw.this.query_id,
|
||||
pw.this.query,
|
||||
pw.this.alert_enabled,
|
||||
prompt=build_prompt(pw.this.documents_list, pw.this.query),
|
||||
)
|
||||
|
||||
responses = prompt.select(
|
||||
pw.this.query_id,
|
||||
pw.this.query,
|
||||
pw.this.alert_enabled,
|
||||
response=model(
|
||||
prompt_chat_single_qa(pw.this.prompt),
|
||||
),
|
||||
)
|
||||
|
||||
output = responses.select(
|
||||
result=construct_message(pw.this.response, pw.this.alert_enabled)
|
||||
)
|
||||
|
||||
# and send the answers back to the asking users
|
||||
response_writer(output)
|
||||
|
||||
# Part IV: send alerts about responses which changed significantly.
|
||||
|
||||
# However, for the queries with alerts the processing continues
|
||||
# whenever the set of documents retrieved for a query changes,
|
||||
# the table of responses is updated.
|
||||
responses = responses.filter(pw.this.alert_enabled)
|
||||
|
||||
def acceptor(new: str, old: str) -> bool:
|
||||
if new == old:
|
||||
return False
|
||||
|
||||
# TODO: clean after udfs can be used as common functions
|
||||
prompt = [dict(role="system", content=build_prompt_compare_answers(new, old))]
|
||||
decision = asyncio.run(model.__wrapped__(prompt, max_tokens=20))
|
||||
return decision_to_bool(decision)
|
||||
|
||||
# Each update is compared with the previous one for deduplication
|
||||
deduplicated_responses = pw.stateful.deduplicate(
|
||||
responses,
|
||||
col=responses.response,
|
||||
acceptor=acceptor,
|
||||
instance=responses.query_id,
|
||||
)
|
||||
|
||||
# Significant alerts are sent to the user
|
||||
alerts = deduplicated_responses.select(
|
||||
message=construct_notification_message(pw.this.query, pw.this.response)
|
||||
)
|
||||
pw.io.slack.send_alerts(alerts.message, slack_alert_channel_id, slack_alert_token)
|
||||
|
||||
# Finally, we execute the computation graph
|
||||
pw.run(monitoring_level=pw.MonitoringLevel.NONE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,19 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
pathway:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
OPENAI_API_KEY:
|
||||
PATHWAY_PERSISTENT_STORAGE:
|
||||
streamlit_ui:
|
||||
depends_on:
|
||||
- pathway
|
||||
build:
|
||||
context: ./ui
|
||||
ports:
|
||||
- "8501:8501"
|
||||
environment:
|
||||
PATHWAY_REST_CONNECTOR_HOST: "pathway"
|
||||
|
After Width: | Height: | Size: 3.8 MiB |
@@ -0,0 +1,11 @@
|
||||
FROM python:3.11
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install streamlit python-dotenv
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8501
|
||||
|
||||
CMD ["streamlit", "run", "server.py", "--server.port", "8501", "--server.address", "0.0.0.0"]
|
||||
@@ -0,0 +1,69 @@
|
||||
import os
|
||||
|
||||
import requests
|
||||
import streamlit as st
|
||||
from dotenv import load_dotenv
|
||||
|
||||
api_host = "localhost"
|
||||
api_port = 8080
|
||||
|
||||
load_dotenv()
|
||||
api_host = os.environ.get("PATHWAY_REST_CONNECTOR_HOST", "127.0.0.1")
|
||||
api_port = int(os.environ.get("PATHWAY_REST_CONNECTOR_PORT", 8080))
|
||||
|
||||
with st.sidebar:
|
||||
st.markdown("## How to query your data\n")
|
||||
st.markdown(
|
||||
"""Enter your question, optionally
|
||||
ask to be alerted.\n"""
|
||||
)
|
||||
st.markdown(
|
||||
"Example: 'When does the magic cola campaign start? Alert me if the start date changes'",
|
||||
)
|
||||
st.markdown(
|
||||
"""[View the source code on GitHub](
|
||||
https://github.com/pathwaycom/llm-app/templates/drive_alert/app.py)"""
|
||||
)
|
||||
st.markdown("## Current Alerts:\n")
|
||||
|
||||
|
||||
# Streamlit UI elements
|
||||
st.title("Google Drive notifications with LLM")
|
||||
|
||||
prompt = st.text_input("How can I help you today?")
|
||||
# prompt = st.chat_input("How can I help you today?")
|
||||
# Initialize chat history
|
||||
if "messages" not in st.session_state:
|
||||
st.session_state.messages = []
|
||||
|
||||
# Display chat messages from history on app rerun
|
||||
for message in st.session_state.messages:
|
||||
with st.chat_message(message["role"]):
|
||||
st.markdown(message["content"])
|
||||
|
||||
|
||||
# React to user input
|
||||
if prompt:
|
||||
# Display user message in chat message container
|
||||
with st.chat_message("user"):
|
||||
st.markdown(prompt)
|
||||
|
||||
# Add user message to chat history
|
||||
st.session_state.messages.append({"role": "user", "content": prompt})
|
||||
|
||||
for message in st.session_state.messages:
|
||||
if message["role"] == "user":
|
||||
st.sidebar.text(f"📩 {message['content']}")
|
||||
|
||||
url = f"http://{api_host}:{api_port}/"
|
||||
data = {"query": prompt, "user": "user"}
|
||||
|
||||
response = requests.post(url, json=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
response = response.json()
|
||||
with st.chat_message("assistant"):
|
||||
st.markdown(response)
|
||||
st.session_state.messages.append({"role": "assistant", "content": response})
|
||||
else:
|
||||
st.error(f"Failed to send data. Status code: {response.status_code}")
|
||||
@@ -0,0 +1 @@
|
||||
OPENAI_API_KEY="sk-***"
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,281 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
# Multimodal RAG with Pathway Live Data Framework: Process your Financial Reports and Tables with GPT-4o
|
||||
|
||||
## **Overview**
|
||||
|
||||
This app template showcases how you can build a multimodal RAG application and launch a document processing pipeline that utilizes a vision language model like the `GPT-4o` for parsing. The Pathway Live Data Framework processes unstructured financial documents within specified directories, extracting and storing the information in a scalable in-memory index. This index is optimized for dynamic RAG, ensuring that search results are continuously updated as documents are modified or new files are added.
|
||||
|
||||
Using this approach, you can make your AI application run in permanent connection with your drive, in sync with your documents which include visually formatted elements: tables, charts, images, etc.
|
||||
|
||||
We specifically use `GPT-4o` to improve the table data extraction accuracy and demonstrate how this approach outperforms the industry-standard RAG toolkits.
|
||||
|
||||
In this showcase, we focused on the finance domain because financial documents often rely heavily on tables in various forms. This showcase highlights the limitations of traditional RAG setups, which struggle to answer questions based on table data. By contrast, our multimodal RAG approach excels in extracting accurate information from tables.
|
||||
|
||||
The following GIF shows a snippet from our experiments:
|
||||
|
||||

|
||||
|
||||
If you want to skip the explanations, you can directly find the code [here](#Running-the-app).
|
||||
|
||||
## Table of contents
|
||||
|
||||
This includes the technical details to the steps to create a REST Endpoint to run the dynamic RAG application via Docker and modify it for your use-cases.
|
||||
|
||||
- [Overview](#Overview)
|
||||
- [Architecture](#Architecture)
|
||||
- [Pipeline Organization](#Pipeline-Organization)
|
||||
- [Customizing the pipeline](#Customizing-the-pipeline)
|
||||
- [Running the app](#Running-the-app)
|
||||
- [Conclusion](#Conclusion)
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||
We use `GPT-4o` in two separate places in the flow of data:
|
||||
- Extracting and understanding the tables inside the PDF
|
||||
- Answering questions with the retrieved context
|
||||
|
||||

|
||||
|
||||
The architecture of this multimodal RAG application involves several key components:
|
||||
|
||||
- **Data Ingestion**: Ingests data from various sources like local folders, Google Drive, or SharePoint.
|
||||
- **Document Parsing and Embedding**: Utilizes `DoclingParser` for parsing documents and `OpenAIEmbedder` for embedding text. This includes handling and processing images within PDFs.
|
||||
- **Document Store**: The `DocumentStoreServer` indexes parsed documents and retrieves relevant chunks for answering questions.
|
||||
- **Question Answering**: Uses the `BaseRAGQuestionAnswerer` class to call `GPT-4o` for generating responses based on the retrieved context.
|
||||
- **Server Setup**: Sets up a REST endpoint to serve the RAG application.
|
||||
|
||||
For more advanced RAG options, make sure to check out [rerankers](https://pathway.com/developers/api-docs/pathway-xpacks-llm/rerankers) and the [adaptive rag example](../adaptive_rag/).
|
||||
|
||||
|
||||
## Pipeline Organization
|
||||
|
||||
This folder contains several objects:
|
||||
- `app.py`, the main application code using Pathway Live Data Framework and written in Python. It reads configuration from `app.yaml`, and runs a server answering queries to the defined pipeline.
|
||||
- `app.yaml`, YAML configuration file, that defines components of the pipeline.
|
||||
- `Dockerfile`, the Docker configuration for running the pipeline in a container. It includes instructions for installing dependencies and setting up the runtime environment.
|
||||
- `requirements.txt`, the dependencies for the pipeline. This file can be passed to `pip install -r requirements.txt` to install everything needed to launch the pipeline locally.
|
||||
- `.env`, a short environment variables configuration file where the OpenAI key must be stored. This file ensures secure handling of sensitive information.
|
||||
- `data/`, a folder with exemplary files that can be used for test runs. It includes sample financial documents to demonstrate the pipeline's capabilities.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway templates, read [our guide](https://pathway.com/developers/user-guide/llm-xpack/yaml-templates).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- LLM
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
Pathway [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
You can also check our other templates - [Question Answering RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag),
|
||||
[Multimodal RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/multimodal_rag) or
|
||||
[Private RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/private_rag). As all of these only differ
|
||||
in the YAML configuration file, you can also use them as an inspiration for your custom pipeline.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### LLM Model
|
||||
|
||||
This template by default uses two llm models - GPT-4.1-mini for answering queries and GPT-4o for parsing tables and images.
|
||||
|
||||
You can replace either of them with other Open AI models, like GPT-4.1 or GPT-5, but keep in mind that the model used for parsing needs to support image input.
|
||||
You can find the whole list on their [models page](https://platform.openai.com/docs/models).
|
||||
|
||||
To change the model of the answering llm, you simply need to change the `model` in the `$llm` variable to the one you want to use, e.g. to use `GPT-5` set:
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-5"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
capacity: 8
|
||||
```
|
||||
|
||||
You can also use different provider, by using different class from [Pathway Live Data Framework LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview),
|
||||
e.g. here is configuration for locally run Mistral model with Ollama.
|
||||
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.LiteLLMChat
|
||||
model: "ollama/mistral"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DiskCache
|
||||
temperature: 0
|
||||
top_p: 1
|
||||
api_base: "http://localhost:11434"
|
||||
```
|
||||
|
||||
You can also change LLM used for parsing in the same way, by changing `!parsing_llm` in `app.yaml`, just keep in mind to use a multimodal model.
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the host and the port of the webserver.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache or persistence, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
persistence_mode: !pw.PersistenceMode.UDF_CACHING
|
||||
persistence_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `data` folder.
|
||||
|
||||
|
||||
|
||||
## Running the app
|
||||
|
||||
> Note: Recommended way of running the Pathway Live Data Framework on Windows is Docker, refer to [Running with the Docker section](#with-docker).
|
||||
|
||||
First, make sure to install the requirements by running:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt -U
|
||||
```
|
||||
Then, create a `.env` file in this directory and put your API key with `OPENAI_API_KEY=sk-...`, or add the `api_key` argument to `OpenAIChat` and `OpenAIEmbedder`.
|
||||
|
||||
Then, simply run with `python app.py` in this directory.
|
||||
|
||||
### With Docker
|
||||
|
||||
First, make sure to have your OpenAI API key in the environment, you can create a `.env` file as mentioned above, or specify the `api_key` argument in the `OpenAIChat` and `OpenAIEmbedder`.
|
||||
|
||||
In order to let the pipeline get updated with each change in local files, you need to mount the `data` folder inside the docker. The following commands show how to do that.
|
||||
|
||||
The following commands will:
|
||||
- mount the `data` folder inside the Docker
|
||||
- build the image
|
||||
- run the app and expose the port `8000`.
|
||||
|
||||
You can omit the ```-v `pwd`/data:/app/data``` part if you are not using local files as a data source.
|
||||
|
||||
```bash
|
||||
# Make sure you are in the right directory.
|
||||
cd templates/multimodal_rag/
|
||||
|
||||
# Build the image in this folder
|
||||
docker build -t rag .
|
||||
|
||||
# Run the image, mount the `data` folder into image and expose the port `8000`
|
||||
docker run -v `pwd`/data:/app/data -p 8000:8000 rag
|
||||
```
|
||||
|
||||
|
||||
## Querying the pipeline
|
||||
|
||||
Follow the [steps below](#running-the-app) to set up the service. This will create a REST endpoint on your selected host and port, running a service that is connected to your file folder, and ready to answer your questions. There are no extra dependencies.
|
||||
|
||||
In this demo, we run the service on localhost (`0.0.0.0:8000`). You can connect your own front end or application to this endpoint. Here, we test the service with `curl`.
|
||||
|
||||
First, let's check the files contained in your folder are currently indexed:
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/list_documents' -H 'accept: */*' -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
This will return the list of files e.g. if you start with the [data folder](./data) provided in the demo, the answer will be as follows:
|
||||
> `[{"modified_at": 1715765613, "owner": "berke", "path": "data/20230203_alphabet_10K.pdf", "seen_at": 1715768762}]`
|
||||
|
||||
In the default app setup, the connected folder is a local file folder. You can add more folders and file sources, such as [Google Drive](https://pathway.com/developers/user-guide/connectors/gdrive-connector#google-drive-connector) or [Sharepoint](https://pathway.com/developers/user-guide/connecting-to-data/connectors#tutorials), by adding a line of code to the template.
|
||||
|
||||
If you now add or remove files from your connected folder, you can repeat the request and see the index file list has been updated automatically. You can look into the logs of the service to see the progress of the indexing of new and modified files. PDF files of 100 pages should normally take under 10 seconds to sync, and the indexing parallelizes if multiple files are added at a single time.
|
||||
|
||||
Now, let's ask a question from one of the tables inside the report. In our tests, regular RAG applications struggled with the tables and couldn't answer to this question correctly.
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "How much was Operating lease cost in 2021?"
|
||||
}'
|
||||
```
|
||||
> `{"response": "$2,699 million"}`
|
||||
|
||||
This response was correct thanks to the initial LLM parsing step.
|
||||
When we check the context that is sent to the LLM, we see that the Pathway Live Data Framework included the table in the context where as other RAG applications failed to include the table.
|
||||
|
||||
|
||||
Let's try another one,
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "What is the operating income for the fiscal year of 2022?"
|
||||
}'
|
||||
```
|
||||
> `{"response": "$74,842 million"}`
|
||||
|
||||
Another example, let's ask a question that can be answered from the table on the 48th page of the PDF.
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "How much was Marketable securities worth in 2021 in the consolidated balance sheets?"
|
||||
}'
|
||||
```
|
||||
> `{"response": "$118,704 million"}`
|
||||
|
||||
|
||||
Now, let's also fetch the context documents,
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "How much was Operating lease cost in 2021?", "return_context_docs": true
|
||||
}'
|
||||
```
|
||||
> `{"response": "$2,699 million", "context_docs": [{"text": "..."}, ...]`
|
||||
|
||||
Looking good!
|
||||
|
||||
## Conclusion
|
||||
|
||||
This showcase demonstrates setting up a powerful RAG pipeline with advanced table parsing capabilities, unlocking new finance use cases. While we've only scratched the surface, there's more to explore:
|
||||
|
||||
- Re-ranking: Prioritize the most relevant results for your specific query.
|
||||
- Knowledge graphs: Leverage relationships between entities to improve understanding.
|
||||
- Hybrid indexing: Combine different indexing strategies for optimal retrieval.
|
||||
- Adaptive reranking: Iteratively enlarge the context for optimal accuracy, see [our example](../adaptive_rag/README.md).
|
||||
Stay tuned for future examples exploring these advanced techniques with Pathway Live Data Framework!
|
||||
|
||||
RAG applications are most effective when tailored to your specific use case. Here's how you can customize yours:
|
||||
|
||||
- Document parsers and splitters: Fine-tune how documents are processed and broken down for analysis.
|
||||
- Indexing and retrieval strategies: Choose the most efficient approach for your data and search needs.
|
||||
- User Interface (UI): Design a user-friendly interface that caters to your end users' workflows.
|
||||
|
||||
Ready to Get Started?
|
||||
|
||||
Let's discuss how we can help you build a powerful, customized RAG application. [Reach us here to talk or request a demo!](https://pathway.com/solutions/slides-ai-search?modal=requestdemo)
|
||||
|
||||
|
||||
## Quick Links:
|
||||
|
||||
- [Pathway Developer Documentation](https://pathway.com/developers/user-guide/introduction/welcome)
|
||||
- [Pathway Live Data Framework App Templates](https://pathway.com/developers/templates)
|
||||
- [Discord Community of Pathway Live Data Framework](https://discord.gg/pathway)
|
||||
- [Pathway Issue Tracker](https://github.com/pathwaycom/pathway/issues)
|
||||
- [End-to-end dynamic RAG pipeline with Pathway](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag)
|
||||
- [Using Pathway Live Data Framework as a retriever with LlamaIndex](https://docs.llamaindex.ai/en/stable/examples/retrievers/pathway_retriever/)
|
||||
|
||||
Make sure to drop a "Star" to our repositories if you found this resource helpful!
|
||||
@@ -0,0 +1,76 @@
|
||||
import logging
|
||||
from warnings import warn
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.question_answering import SummaryQuestionAnswerer
|
||||
from pathway.xpacks.llm.servers import QASummaryRestServer
|
||||
from pydantic import BaseModel, ConfigDict, InstanceOf
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
question_answerer: InstanceOf[SummaryQuestionAnswerer]
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
with_cache: bool | None = None # deprecated
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
terminate_on_error: bool = False
|
||||
|
||||
def run(self) -> None:
|
||||
server = QASummaryRestServer( # noqa: F841
|
||||
self.host, self.port, self.question_answerer
|
||||
)
|
||||
|
||||
if self.persistence_mode is None:
|
||||
if self.with_cache is True:
|
||||
warn(
|
||||
"`with_cache` is deprecated. Please use `persistence_mode` instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
persistence_mode = pw.PersistenceMode.UDF_CACHING
|
||||
else:
|
||||
persistence_mode = None
|
||||
else:
|
||||
persistence_mode = self.persistence_mode
|
||||
|
||||
if persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
|
||||
pw.run(
|
||||
persistence_config=persistence_config,
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,125 @@
|
||||
# This YAML configuration file is used to set up and configure the Multimodal RAG template.
|
||||
# It defines various components such as data sources, language models, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for generating responses.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: data
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
|
||||
|
||||
# Configures the LLM model settings for generating responses.
|
||||
# The list of available Pathway LLM wrappers is available here:
|
||||
# https://pathway.com/developers/api-docs/pathway-xpacks-llm/llms
|
||||
# You can learn more about those in our documentation:
|
||||
# https://pathway.com/developers/templates/rag-customization/llm-chats
|
||||
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-4.1-mini"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
capacity: 8
|
||||
async_mode: "fully_async"
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.OpenAIEmbedder
|
||||
model: "text-embedding-3-small"
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy {}
|
||||
|
||||
# Defines the llm to be used for parsing images.
|
||||
$parsing_llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-4o"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
async_mode: "fully_async"
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
multimodal_llm: $parsing_llm
|
||||
image_parsing_strategy: "llm"
|
||||
table_parsing_strategy: "llm"
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the splitter for chunking the documents.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Configures the question-answering component using the RAG approach.
|
||||
question_answerer: !pw.xpacks.llm.question_answering.BaseRAGQuestionAnswerer
|
||||
llm: $llm
|
||||
indexer: $document_store
|
||||
# You can set the number of documents to be included as the context of the query
|
||||
# search_topk: 6
|
||||
# You can use your own prompt for querying.
|
||||
# For that set prompt_template to string with `{query}` used as a placeholder for the question,
|
||||
# and `{context}` as a placeholder for context documents.
|
||||
# prompt_template: "Given these documents: {context}, please answer the question: {query}"
|
||||
|
||||
# Change host and port of the webserver by uncommenting these lines
|
||||
# host: "0.0.0.0"
|
||||
# port: 8000
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 4.3 MiB |
@@ -0,0 +1,3 @@
|
||||
pathway[all]
|
||||
python-dotenv~=1.0
|
||||
mpmath~=1.3
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM pathwaycom/pathway:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,249 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
# Fully private RAG with Pathway Live Data Framework
|
||||
|
||||
## **Overview**
|
||||
|
||||
Retrieval-Augmented Generation (RAG) is a powerful method for answering questions using a private knowledge database. Ensuring data security is essential, especially for sensitive information like trade secrets, confidential IP, GDPR-protected data, and internal documents. This showcase demonstrates setting up a private RAG pipeline with adaptive retrieval using Pathway Live Data Framework, Mistral, and Ollama. The provided code deploys this adaptive RAG technique with Pathway Live Data Framework, ensuring no API access or data leaves the local machine.
|
||||
|
||||
The app utilizes modules under `pathway.xpacks.llm`. The `BaseRAGQuestionAnswerer` class is the foundation for building RAG applications with the Pathway Live Data Framework vector store and xpack components, enabling a quick start with RAG applications.
|
||||
|
||||
This example uses `AdaptiveRAGQuestionAnswerer`, an extension of `BaseRAGQuestionAnswerer` with adaptive retrieval. For more on building and deploying RAG applications with Pathway Live Data Framework, including containerization, refer to the demo on question answering.
|
||||
|
||||
The application responds to requests at the `/v2/answer` endpoint. The `answer_query` function takes the `pw_ai_queries` table as input, containing prompts and other arguments from the post request. This table's data is used to call the adaptive retrieval logic.
|
||||
|
||||
The `AdaptiveRAGQuestionAnswerer` implementation under `pathway.xpacks.llm.question_answering` builds a RAG app with the Pathway Live Data Framework vector store and components. It supports two question answering strategies, short (concise) and long (detailed) responses, set during the post request. It allows LLM agnosticity, giving the freedom to choose between proprietary or open-source LLMs. It adapts the number of chunks used as a context, starting with `n_starting_documents` chunks and increasing until an answer is found.
|
||||
|
||||
To learn more about building & deploying RAG applications with Pathway Live Data Framework, including containerization, refer to [demo question answering](../question_answering_rag/README.md).
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
This includes the technical details to the steps to create a REST Endpoint to run the application via Docker and modify it for your use-cases.
|
||||
|
||||
- [Overview](#Overview)
|
||||
- [Architecture](#Architecture)
|
||||
- [Deploying and using a local LLM](#Deploying-and-using-a-local-LLM)
|
||||
- [Running the app](#Running-the-app)
|
||||
- [Querying the app/pipeline](#Querying-the-app/pipeline)
|
||||
- [Modifying the code](#Modifying-the-code)
|
||||
- [Conclusion](#Conclusion)
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
||||
The architecture consists of two connected technology bricks, which will run as services on your machine:
|
||||
- Pathway Live Data Framework brings support for real-time data synchronization pipelines out of the box, and the possibility of secure private document handling with enterprise connectors for synchronizing Sharepoint and Google Drive incrementally. The Pathway Live Data Framework service you'll run performs live document indexing pipeline, and will use Pathway Live Data Framework’s built-in vector store.
|
||||
- The language model you use will be a Mistral 7B, which you will locally deploy as an Ollama service. This model was chosen for its performance and compact size.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway templates, read [our guide](https://pathway.com/developers/templates/configure-yaml).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- LLM
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
Pathway [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
You can also check our other templates - [Question Answering RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag),
|
||||
[Multimodal RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/multimodal_rag) or
|
||||
[Private RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/private_rag). As all of these only differ
|
||||
in the YAML configuration file, you can also use them as an inspiration for your custom pipeline.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### LLM Model
|
||||
|
||||
This template is prepared to run by default locally. However, the pipeline is LLM model agnostic, so you can change them to use other locally deployed model, or even
|
||||
use LLM model available through API calls. For discussion on models used in this template check [the dedicated Section](#deploying-and-using-a-local-LLM).
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the host and the port of the webserver.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache or persistence, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
persistence_mode: !pw.PersistenceMode.UDF_CACHING
|
||||
persistence_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `data` folder.
|
||||
|
||||
#### Local Data Source
|
||||
|
||||
The local data source is configured by using map with tag `!pw.io.fs.read`. Then set `path` to denote the path to a folder with files to be indexed.
|
||||
|
||||
#### Google Drive Data Source
|
||||
|
||||
The Google Drive data source is enabled by using map with tag `!pw.io.gdrive.read`. The map must contain two main parameters:
|
||||
- `object_id`, containing the ID of the folder that needs to be indexed. It can be found from the URL in the web interface, where it's the last part of the address. For example, the publicly available demo folder in Google Drive has the URL `https://drive.google.com/drive/folders/1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`. Consequently, the last part of this address is `1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`, hence this is the `object_id` you would need to specify.
|
||||
- `service_user_credentials_file`, containing the path to the credentials files for the Google [service account](https://cloud.google.com/iam/docs/service-account-overview). To get more details on setting up the service account and getting credentials, you can also refer to [this tutorial](https://pathway.com/developers/user-guide/connectors/gdrive-connector#setting-up-google-drive).
|
||||
|
||||
Besides, to speed up the indexing process you may want to specify the `refresh_interval` parameter, denoted by an integer number of seconds. It corresponds to the frequency between two sequential folder scans. If unset, it defaults to 30 seconds.
|
||||
|
||||
For the full list of the available parameters, please refer to the Google Drive connector [documentation](https://pathway.com/developers/api-docs/pathway-io/gdrive#pathway.io.gdrive.read).
|
||||
|
||||
#### SharePoint Data Source
|
||||
|
||||
This data source requires Scale or Enterprise [license key](https://pathway.com/pricing) - you can obtain free Scale key on [Pathway website](https://pathway.com/get-license).
|
||||
|
||||
To use it, set the map tag to be `!pw.xpacks.connectors.sharepoint.read`, and then provide values of `url`, `tenant`, `client_id`, `cert_path`, `thumbprint` and `root_path`. To read about the meaning of these arguments, check the Sharepoint connector [documentation](https://pathway.com/developers/api-docs/pathway-xpacks-sharepoint#pathway.xpacks.connectors.sharepoint.read).
|
||||
|
||||
|
||||
## Deploying and using a local LLM
|
||||
|
||||
### Embedding Model Selection
|
||||
|
||||
You will use `pathway.xpacks.llm.embedders` module to load open-source embedding models from the HuggingFace model library. For this showcase, pick the `avsolatorio/GIST-small-Embedding-v0` model which has a dimension of 384 as it is compact and performed well in our tests.
|
||||
|
||||
```yaml
|
||||
$embedding_model: "avsolatorio/GIST-small-Embedding-v0"
|
||||
|
||||
$embedder: !pw.xpacks.llm.embedders.SentenceTransformerEmbedder
|
||||
model: $embedding_model
|
||||
call_kwargs:
|
||||
show_progress_bar: False
|
||||
```
|
||||
|
||||
If you would like to use a higher-dimensional model, here are some possible alternatives you could use instead:
|
||||
|
||||
- mixedbread-ai/mxbai-embed-large-v1
|
||||
- avsolatorio/GIST-Embedding-v0
|
||||
|
||||
For other possible choices, take a look at the [MTEB Leaderboard](https://huggingface.co/spaces/mteb/leaderboard) managed by HuggingFace.
|
||||
|
||||
|
||||
### Local LLM Deployment
|
||||
|
||||
Due to its size and performance it is best to run the `Mistral 7B` LLM. Here you would deploy it as a service running on GPU, using `Ollama`.
|
||||
|
||||
To run local LLM, you can refer to these steps:
|
||||
- Download Ollama from [ollama.com/download](https://ollama.com/download)
|
||||
- In your terminal, run `ollama serve`
|
||||
- In another terminal, run `ollama run mistral`
|
||||
|
||||
You can now test it with the following request:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:11434/api/generate -d '{
|
||||
"model": "mistral",
|
||||
"prompt":"Here is a story about llamas eating grass"
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
### LLM Initialization
|
||||
|
||||
Now you will initialize the LLM instance that will call the local model.
|
||||
|
||||
```yaml
|
||||
$llm_model: "ollama/mistral"
|
||||
|
||||
$llm: !pw.xpacks.llm.llms.LiteLLMChat
|
||||
model: $llm_model
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
api_base: "http://localhost:11434"
|
||||
# api_base: "http://host.docker.internal:11434" # use this when you are running the app in the Docker on Mac or Windows
|
||||
async_mode: "fully_async"
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
First, make sure your local LLM is up and running. By default, the pipeline tries to access the LLM at `http://localhost:11434`. You can change that by setting `api_base` value in the app.yaml file.
|
||||
|
||||
### With Docker
|
||||
In order to let the pipeline get updated with each change in local files, you need to mount the folder onto the docker. The following commands show how to do that.
|
||||
|
||||
#### Linux
|
||||
```bash
|
||||
# Build the image in this folder
|
||||
docker build -t privaterag .
|
||||
|
||||
# Run the image, mount the `data` folder into image
|
||||
docker run --net host -v ./data:/app/data privaterag
|
||||
```
|
||||
|
||||
#### Mac or Windows
|
||||
In the `app.yaml` change `api_base` to be `http://host.docker.internal:11434`. Then run:
|
||||
|
||||
```bash
|
||||
# Build the image in this folder
|
||||
docker build -t privaterag .
|
||||
|
||||
# Run the image, mount the `data` folder into image
|
||||
docker run -v ./data:/app/data -p 8000:8000 privaterag
|
||||
```
|
||||
|
||||
### Locally
|
||||
To run locally you need to install the Pathway Live Data Framework app with LLM dependencies using:
|
||||
```bash
|
||||
pip install pathway[all]
|
||||
```
|
||||
|
||||
Then change your directory in the terminal to this folder and run the app:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
## Querying the app/pipeline
|
||||
|
||||
Finally, query the application with;
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://0.0.0.0:8000/v2/answer' -H 'accept: */*' -H 'Content-Type: application/json' -d '{
|
||||
"prompt": "What is the start date of the contract?"
|
||||
}'
|
||||
```
|
||||
> `{"response": "December 21, 2015 [6]"}`
|
||||
|
||||
|
||||
## Conclusion:
|
||||
|
||||
Now you have a fully private RAG set up with Pathway Live Data Framework and Ollama. All your data remains safe on your system. Moreover, the set-up is optimized for speed, thanks to how Ollama runs the LLM, and how Pathway Live Data Framework’s adaptive retrieval mechanism reduces token consumption while preserving the accuracy of the RAG.
|
||||
|
||||
This is a full production-ready set-up which includes reading your data sources, parsing the data, and serving the endpoint.
|
||||
This private RAG setup can be run entirely locally with open-source LLMs, making it ideal for organizations with sensitive data and explainable AI needs.
|
||||
|
||||
## Quick Links:
|
||||
|
||||
- [Pathway Developer Documentation](https://pathway.com/developers/user-guide/introduction/welcome)
|
||||
- [Pathway Live Data Framework App Templates](https://pathway.com/developers/templates)
|
||||
- [Discord Community of Pathway Live Data Framework](https://discord.gg/pathway)
|
||||
- [Pathway Issue Tracker](https://github.com/pathwaycom/pathway/issues)
|
||||
- [End-to-end dynamic RAG pipeline with Pathway](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag)
|
||||
- [Using Pathway Live Data Framework as a vector store with Langchain](https://python.langchain.com/v0.2/docs/integrations/vectorstores/pathway/)
|
||||
- [Using Pathway Live Data Framework as a retriever with LlamaIndex](https://docs.llamaindex.ai/en/stable/examples/retrievers/pathway_retriever/)
|
||||
|
||||
Make sure to drop a “Star” to our repositories if you found this resource helpful!
|
||||
@@ -0,0 +1,76 @@
|
||||
import logging
|
||||
from warnings import warn
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.question_answering import SummaryQuestionAnswerer
|
||||
from pathway.xpacks.llm.servers import QASummaryRestServer
|
||||
from pydantic import BaseModel, ConfigDict, InstanceOf
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
question_answerer: InstanceOf[SummaryQuestionAnswerer]
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
with_cache: bool | None = None # deprecated
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
terminate_on_error: bool = False
|
||||
|
||||
def run(self) -> None:
|
||||
server = QASummaryRestServer( # noqa: F841
|
||||
self.host, self.port, self.question_answerer
|
||||
)
|
||||
|
||||
if self.persistence_mode is None:
|
||||
if self.with_cache is True:
|
||||
warn(
|
||||
"`with_cache` is deprecated. Please use `persistence_mode` instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
persistence_mode = pw.PersistenceMode.UDF_CACHING
|
||||
else:
|
||||
persistence_mode = None
|
||||
else:
|
||||
persistence_mode = self.persistence_mode
|
||||
|
||||
if persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
|
||||
pw.run(
|
||||
persistence_config=persistence_config,
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,119 @@
|
||||
# This YAML configuration file is used to set up and configure the Private RAG template.
|
||||
# It defines various components such as data sources, language models, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for generating responses.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: data
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
|
||||
|
||||
# Configures the LLM model settings for generating responses.
|
||||
# The list of available Pathway LLM wrappers is available here:
|
||||
# https://pathway.com/developers/api-docs/pathway-xpacks-llm/llms
|
||||
# You can learn more about those in our documentation:
|
||||
# https://pathway.com/developers/templates/rag-customization/llm-chats
|
||||
|
||||
|
||||
$llm_model: "ollama/mistral"
|
||||
|
||||
$llm: !pw.xpacks.llm.llms.LiteLLMChat
|
||||
model: $llm_model
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
api_base: "http://localhost:11434"
|
||||
# api_base: "http://host.docker.internal:11434" # use this when you are running the app in the Docker on Mac or Windows
|
||||
async_mode: "fully_async"
|
||||
|
||||
$embedding_model: "avsolatorio/GIST-small-Embedding-v0"
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.SentenceTransformerEmbedder
|
||||
model: $embedding_model
|
||||
call_kwargs:
|
||||
show_progress_bar: False
|
||||
|
||||
# Sets up the splitter for chunking the documents.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
table_parsing_strategy: "llm"
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Configures the question-answering component using the RAG approach.
|
||||
question_answerer: !pw.xpacks.llm.question_answering.AdaptiveRAGQuestionAnswerer
|
||||
llm: $llm
|
||||
indexer: $document_store
|
||||
n_starting_documents: 2
|
||||
factor: 2
|
||||
max_iterations: 4
|
||||
strict_prompt: true
|
||||
|
||||
# Change host and port of the webserver by uncommenting these lines
|
||||
# host: "0.0.0.0"
|
||||
# port: 8000
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
|
After Width: | Height: | Size: 375 KiB |
@@ -0,0 +1,3 @@
|
||||
pathway[all]
|
||||
python-dotenv~=1.0
|
||||
mpmath~=1.3
|
||||
@@ -0,0 +1 @@
|
||||
OPENAI_API_KEY="sk-***"
|
||||
@@ -0,0 +1,16 @@
|
||||
ARG PATHWAY_SRC_IMAGE=pathwaycom/pathway:latest
|
||||
|
||||
FROM ${PATHWAY_SRC_IMAGE}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y python3-opencv tesseract-ocr-eng poppler-utils libreoffice \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,422 @@
|
||||
<p align="center" class="flex items-center gap-1 justify-center flex-wrap">
|
||||
<img src="../../assets/gcp-logo.svg?raw=true" alt="GCP Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/gcp-deploy">Deploy with GCP</a> |
|
||||
<img src="../../assets/aws-fargate-logo.svg?raw=true" alt="AWS Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/aws-fargate-deploy">Deploy with AWS</a> |
|
||||
<img src="../../assets/azure-logo.svg?raw=true" alt="Azure Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/azure-aci-deploy">Deploy with Azure</a> |
|
||||
<img src="../../assets/render.png?raw=true" alt="Render Logo" height="20" width="20">
|
||||
<a href="https://pathway.com/developers/user-guide/deployment/render-deploy"> Deploy with Render </a>
|
||||
</p>
|
||||
|
||||
# Pathway Live Data Framework RAG app with always up-to-date knowledge
|
||||
|
||||
This demo shows how to create a real-time RAG application using [Pathway](https://github.com/pathwaycom/pathway) that provides always up-to-date knowledge to your LLM without the need for a separate ETL.
|
||||
|
||||
You will see a running example of how to get started with the Pathway Live Data Framework vector store that eliminates the need for ETL pipelines which are needed in the regular VectorDBs.
|
||||
This significantly reduces the developer's workload.
|
||||
|
||||
This demo allows you to:
|
||||
|
||||
- Create a Document store with real-time document indexing from Google Drive, Microsoft 365 SharePoint, or a local directory;
|
||||
- Connect an OpenAI LLM model of choice to your knowledge base;
|
||||
- Get quality, accurate, and precise responses to your questions;
|
||||
- Ask questions about folders, files or all your documents easily, with the help of filtering options;
|
||||
- Use LLMs over API to summarize texts;
|
||||
- Get an executive outlook for a question on different files to easily access available knowledge in your documents;
|
||||
|
||||
|
||||
Note: This app relies on [Document Store](https://pathway.com/developers/api-docs/pathway-xpacks-llm/document_store) to learn more, you can check out [this blog post](https://pathway.com/developers/user-guide/llm-xpack/docs-indexing).
|
||||
|
||||
## Table of contents
|
||||
- [Summary of available endpoints](#Summary-of-available-endpoints)
|
||||
- [How it works](#How-it-works)
|
||||
- [Customizing the pipeline](#Customizing-the-pipeline)
|
||||
- [How to run the project](#How-to-run-the-project)
|
||||
- [Using the app](#Query-the-documents)
|
||||
|
||||
|
||||
## Summary of available endpoints
|
||||
|
||||
This example spawns a lightweight webserver using Pathway’s [`QASummaryRestServer`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/servers#pathway.xpacks.llm.servers.QASummaryRestServer) that accepts queries on five possible endpoints, divided into two categories: document indexing and RAG with LLM.
|
||||
|
||||
### Document Indexing capabilities
|
||||
- `/v1/retrieve` to perform similarity search;
|
||||
- `/v1/statistics` to get the basic stats about the indexer's health;
|
||||
- `/v2/list_documents` to retrieve the metadata of all files currently processed by the indexer.
|
||||
|
||||
### LLM and RAG capabilities
|
||||
- `/v2/answer` to ask questions about your documents, or directly talk with your LLM;
|
||||
- `/v2/summarize` to summarize a list of texts;
|
||||
|
||||
See the [using the app section](###Using-the-app) to learn how to use the provided endpoints.
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Data Ingestion**
|
||||
We define one or more sources in `app.yaml` (local directories, Google Drive, Microsoft SharePoint, etc.).
|
||||
- The provided demo references a local folder `data/` by default.
|
||||
- The code can poll these sources at configured intervals, so when new documents appear or existing ones change, they are automatically parsed and re-indexed in real-time.
|
||||
|
||||
2. **Parsing & Splitting**
|
||||
Using [Docling](https://www.docling.ai/) (through Pathway’s [`DoclingParser`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/parsers#pathway.xpacks.llm.parsers.DoclingParser)) and [TokenCountSplitter](https://pathway.com/developers/api-docs/pathway-xpacks-llm/splitters#pathway.xpacks.llm.splitters.TokenCountSplitter), documents are parsed and chunked into smaller parts.
|
||||
|
||||
3. **Embedding**
|
||||
Via [`OpenAIEmbedder`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/embedders#pathway.xpacks.llm.embedders.OpenAIEmbedder), the chunks get turned into embeddings. You can substitute your own embedder if you wish.
|
||||
|
||||
4. **Indexing**
|
||||
Using [`USearchKnnFactory`](https://pathway.com/developers/api-docs/indexing#pathway.stdlib.indexing.UsearchKnnFactory), the embeddings are stored in a vector index. This is all streaming as well, so new embeddings are added or updated automatically.
|
||||
|
||||
5. **Serving**
|
||||
- We create a [`SummaryQuestionAnswerer`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/question_answering#pathway.xpacks.llm.question_answering.SummaryQuestionAnswerer) (as specified in `app.py`), which can handle both question-answering and summarization requests.
|
||||
- A web server, [`QASummaryRestServer`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/servers#pathway.xpacks.llm.servers.QASummaryRestServer), exposes multiple endpoints for retrieval, Q&A, summarization, and more.
|
||||
|
||||
Because Pathway Live Data Framework is fully incremental, any changes to your source files immediately flow through parsing, embedding, indexing, and ultimately get reflected in the answers from the LLM. The user can then query the created index with simple HTTP requests to the endpoints mentioned above.
|
||||
|
||||
## Pipeline Organization
|
||||
|
||||
This folder contains several objects:
|
||||
- `app.py`, the application code using Pathway Live Data Framework and written in Python;
|
||||
- `app.yaml`, the file containing configuration of the pipeline, like LLM models, sources or server address;
|
||||
- `requirements.txt`, the dependencies for the pipeline. It can be passed to `pip install -r requirements.txt` to install everything that is needed to launch the pipeline locally;
|
||||
- `Dockerfile`, the Docker configuration for running the pipeline in the container;
|
||||
- `.env`, a short environment variables configuration file where the OpenAI key must be stored;
|
||||
- `data/`, a folder with exemplary files that can be used for the test runs.
|
||||
- `ui/`, a simple ui written in Streamlit for asking questions.
|
||||
|
||||
## Pathway Live Data Framework tooling
|
||||
### Prompts and helpers
|
||||
|
||||
Pathway allows you to define custom prompts in addition to the ones provided in [`pathway.xpacks.llm`](https://pathway.com/developers/user-guide/llm-xpack/overview).
|
||||
|
||||
You can also use user-defined functions using the [`@pw.udf`](https://pathway.com/developers/api-docs/pathway#pathway.udf) decorator to define custom functions that will run on streaming data.
|
||||
|
||||
### RAG
|
||||
|
||||
Pathway provides all the tools to create a RAG application and query it: a [Pathway Document store](https://pathway.com/developers/api-docs/pathway-xpacks-llm/document_store#pathway.xpacks.llm.document_store.DocumentStore) and a web server (defined with the [REST connector](https://pathway.com/developers/api-docs/pathway-io/http#pathway.io.http.rest_connector)).
|
||||
For the sake of the demo, we kept the app simple, consisting of the main components you would find in a regular RAG application. It can be further enhanced with query writing methods, re-ranking layer and custom splitting steps.
|
||||
|
||||
Don't hesitate to take a look at our [documentation](https://pathway.com/developers/user-guide/introduction/welcome) to learn how Pathway works.
|
||||
|
||||
|
||||
## OpenAI API Key Configuration
|
||||
|
||||
Default LLM provider in this template is OpenAI, so, unless you change the configuration, you need to provide OpenAI API key. Please configure your key in a `.env` file by providing it as follows: `OPENAI_API_KEY=sk-*******`. You can refer to the stub file `.env` in this repository, where you will need to paste your key instead of `sk-*******`.
|
||||
|
||||
## Customizing the pipeline
|
||||
|
||||
The code can be modified by changing the `app.yaml` configuration file. To read more about YAML files used in Pathway templates, read [our guide](https://pathway.com/developers/templates/configure-yaml).
|
||||
|
||||
In the `app.yaml` file we define:
|
||||
- input connectors
|
||||
- LLM
|
||||
- embedder
|
||||
- index
|
||||
and any of these can be replaced or, if no longer needed, removed. For components that can be used check
|
||||
Pathway [LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview), or you can implement your own.
|
||||
|
||||
You can also check our other templates - [Question Answering RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag),
|
||||
[Multimodal RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/multimodal_rag) or
|
||||
[Private RAG](https://github.com/pathwaycom/llm-app/tree/main/templates/private_rag). As all of these only differ
|
||||
in the YAML configuration file, you can also use them as an inspiration for your custom pipeline.
|
||||
|
||||
Here some examples of what can be modified.
|
||||
|
||||
### LLM Model
|
||||
|
||||
You can choose any of the models offered by Open AI, like GPT-5, GPT-4.1, or GPT-4o.
|
||||
You can find the whole list on their [models page](https://platform.openai.com/docs/models).
|
||||
|
||||
You simply need to change the `model` to the one you want to use, e.g., to use GPT-5:
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-5"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache
|
||||
capacity: 8
|
||||
```
|
||||
|
||||
The default model is `gpt-4.1-mini`.
|
||||
|
||||
You can also use different provider, by using different class from [Pathway Live Data Framework LLM xpack](https://pathway.com/developers/user-guide/llm-xpack/overview),
|
||||
e.g. here is configuration for locally run Mistral model.
|
||||
|
||||
```yaml
|
||||
$llm: !pw.xpacks.llm.llms.LiteLLMChat
|
||||
model: "ollama/mistral"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DiskCache
|
||||
temperature: 0
|
||||
top_p: 1
|
||||
api_base: "http://localhost:11434"
|
||||
```
|
||||
|
||||
### Index of your choice
|
||||
|
||||
The [`DocumentStore`](https://pathway.com/developers/api-docs/pathway-xpacks-llm/document_store#pathway.xpacks.llm.document_store.DocumentStore) makes building and customizing a document indexing pipeline straightforward. It processes documents and enables querying the closest documents to a given query based on your chosen indexing strategy. Here's how you can use a hybrid indexing approach, combining vector-based and text-based retrieval:
|
||||
|
||||
Example: Hybrid Indexing with `USearchKNN` and `TantivyBM25`
|
||||
|
||||
The following example demonstrates how to configure and use the [HybridIndex](https://pathway.com/developers/api-docs/indexing#pathway.stdlib.indexing.HybridIndex) that combines:
|
||||
|
||||
- **`USearchKNN`**: A vector-based index leveraging embeddings for semantic similarity search.
|
||||
- **[`TantivyBM25`](https://pathway.com/developers/api-docs/indexing#pathway.stdlib.indexing.TantivyBM25)**: A text-based index using BM25 for keyword matching.
|
||||
|
||||
|
||||
```yaml
|
||||
$knn_index: !pw.indexing.USearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchKnnMetricKind.COS
|
||||
dimensions: 1536
|
||||
|
||||
$bm25_index: !pw.indexing.TantivyBM25Factory
|
||||
|
||||
$hybrid_index_factory: !pw.indexing.HybridIndexFactory
|
||||
retriever_factories:
|
||||
- $knn_index
|
||||
- $bm25_index
|
||||
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $hybrid_index_factory
|
||||
```
|
||||
Choose the indexing strategy that fits your requirements with `DocumentStore`
|
||||
|
||||
### Webserver
|
||||
|
||||
You can configure the host and the port of the webserver.
|
||||
Here is the default configuration:
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
You can configure whether you want to enable cache or persistence, to avoid repeated API accesses, and where the cache is stored.
|
||||
Default values:
|
||||
```yaml
|
||||
persistence_mode: !pw.PersistenceMode.UDF_CACHING
|
||||
persistence_backend: !pw.persistence.Backend.filesystem
|
||||
path: ".Cache"
|
||||
```
|
||||
|
||||
### Data sources
|
||||
|
||||
You can configure the data sources by changing `$sources` in `app.yaml`.
|
||||
You can add as many data sources as you want. You can have several sources of the same kind, for instance, several local sources from different folders.
|
||||
The sections below describe how to configure local, Google Drive and Sharepoint source, and you can check the examples of YAML configuration in our [user guide](https://pathway.com/developers/templates/yaml-snippets/data-sources-examples/). While these are not described in this Section, you can also use any input [connector](https://pathway.com/developers/user-guide/connecting-to-data/connectors) from Pathway package.
|
||||
|
||||
By default, the app uses a local data source to read documents from the `data` folder.
|
||||
|
||||
#### Local Data Source
|
||||
|
||||
The local data source is configured by using map with tag `!pw.io.fs.read`. Then set `path` to denote the path to a folder with files to be indexed.
|
||||
|
||||
#### Google Drive Data Source
|
||||
|
||||
The Google Drive data source is enabled by using map with tag `!pw.io.gdrive.read`. The map must contain two main parameters:
|
||||
- `object_id`, containing the ID of the folder that needs to be indexed. It can be found from the URL in the web interface, where it's the last part of the address. For example, the publicly available demo folder in Google Drive has the URL `https://drive.google.com/drive/folders/1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs`. The last part of this address is `1cULDv2OaViJBmOfG5WB0oWcgayNrGtVs` and this is the `object_id` you would need to specify.
|
||||
- `service_user_credentials_file`, containing the path to the credentials files for the Google [service account](https://cloud.google.com/iam/docs/service-account-overview). To get more details on setting up the service account and getting credentials, you can also refer to [this tutorial](https://pathway.com/developers/user-guide/connectors/gdrive-connector#setting-up-google-drive).
|
||||
|
||||
Besides, to speed up the indexing process you may want to specify the `refresh_interval` parameter, denoted by an integer number of seconds. It corresponds to the frequency between two sequential folder scans. If unset, it defaults to 30 seconds.
|
||||
|
||||
For the full list of the available parameters, please refer to the Google Drive connector [documentation](https://pathway.com/developers/api-docs/pathway-io/gdrive#pathway.io.gdrive.read).
|
||||
|
||||
#### SharePoint Data Source
|
||||
|
||||
This data source requires Scale or Enterprise [license key](https://pathway.com/pricing) - you can obtain free Scale key on [Pathway website](https://pathway.com/get-license).
|
||||
|
||||
To use it, set the map tag to be `!pw.xpacks.connectors.sharepoint.read`, and then provide values of `url`, `tenant`, `client_id`, `cert_path`, `thumbprint` and `root_path`. To read about the meaning of these arguments, check the Sharepoint connector [documentation](https://pathway.com/developers/api-docs/pathway-xpacks-sharepoint#pathway.xpacks.connectors.sharepoint.read).
|
||||
|
||||
## How to run the project
|
||||
|
||||
Clone the llm-app repository from GitHub. This repository contains all the files you’ll need.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/pathwaycom/llm-app.git
|
||||
```
|
||||
|
||||
### Locally
|
||||
If you are on Windows, please refer to [running with docker](#With-Docker) section below.
|
||||
|
||||
To run locally, change your directory in the terminal to this folder. Then, run the app with `python`.
|
||||
|
||||
```bash
|
||||
cd templates/question_answering_rag
|
||||
|
||||
python app.py
|
||||
```
|
||||
|
||||
Please note that the local run requires the dependencies to be installed. It can be done with a simple pip command:
|
||||
`pip install -r requirements.txt`
|
||||
|
||||
### With Docker
|
||||
|
||||
Build the Docker with:
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
And, run with:
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
This will start the pipeline and the ui for asking questions.
|
||||
|
||||
### Query the documents
|
||||
You will see the logs for parsing & embedding documents in the Docker image logs.
|
||||
Give it a few minutes to finish up on embeddings, you will see `0 entries (x minibatch(es)) have been...` message.
|
||||
If there are no more updates, this means the app is ready for use!
|
||||
|
||||
To test it, let's query the stats:
|
||||
```bash
|
||||
curl -X 'POST' 'http://localhost:8000/v1/statistics' -H 'accept: */*' -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
For more information on available endpoints by default, see [above](#Summary-of-available-endpoints).
|
||||
|
||||
We provide some example `curl` queries to start with.
|
||||
|
||||
The general structure is sending a request to `http://{HOST}:{PORT}/{ENDPOINT}`.
|
||||
|
||||
Where HOST is the `host` variable you specify in your app configuration. PORT is the `port` number you are running your app on, and ENDPOINT is the specific extension for endpoints. They are specified in the application code, and they are listed with the versioning as `/v1/...`.
|
||||
|
||||
Note that, if you are using the Pathway Live Data Framework hosted version, you should send requests to `https://...` rather than `http://...` and emit the `:{PORT}` part of the URL.
|
||||
|
||||
You need to add two headers, `-H 'accept: */*' -H 'Content-Type: application/json'`.
|
||||
|
||||
Finally, for endpoints that expect data in the query, you can pass it with `-d '{key: value}'` format.
|
||||
|
||||
#### Listing inputs
|
||||
Get the list of available inputs and associated metadata.
|
||||
|
||||
```bash
|
||||
curl -X 'POST' 'http://localhost:8000/v2/list_documents' -H 'accept: */*' -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
#### Searching in your documents
|
||||
|
||||
Search API gives you the ability to search in available inputs and get up-to-date knowledge.
|
||||
`query` is the search query you want to execute.
|
||||
|
||||
`k` (optional) is an integer, the number of documents to be retrieved. Documents in this case means small chunks that are stored in your vector store.
|
||||
|
||||
`metadata_filter` (optional) String to filter results with Jmespath query.
|
||||
|
||||
`filepath_globpattern` (optional) String to filter results with globbing pattern. For example `"*"` would result in no filter, `"*.docx"` would result in only `docx` files being retrieved.
|
||||
|
||||
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v1/retrieve' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"query": "Which articles of General Data Protection Regulation are relevant for clinical trials?",
|
||||
"k": 6
|
||||
}'
|
||||
```
|
||||
|
||||
#### Asking questions to LLM (With and without RAG)
|
||||
|
||||
- Note: The local version of this app does not require `openai_api_key` parameter in the payload of the query. Embedder and LLM will use the API key in the `.env` file. However, Pathway hosted public demo available on the [website](https://pathway.com/solutions/ai-pipelines) requires a valid `openai_api_key` to execute the query.
|
||||
|
||||
- Note: All of the RAG endpoints use the `model` provided in the config by default, however, you can specify another model with the `model` parameter in the payload to use a different one for generating the response.
|
||||
|
||||
For question answering without any context, simply omit `filters` key in the payload and send the following request.
|
||||
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v2/answer' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"prompt": "What is the start date of the contract?"
|
||||
}'
|
||||
```
|
||||
|
||||
Question answering with the knowledge from a specific file, based on the path.
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v2/answer' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"prompt": "What is the start date of the contract?",
|
||||
"filters": "globmatch(`"IdeanomicsInc_20160330_10-K_EX-10.26_9512211_EX-10.26_Content License Agreement.pdf"`, path)"
|
||||
}'
|
||||
```
|
||||
|
||||
Alternatively, with the knowledge from files that have the word `Ide` in their paths.
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v2/answer' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"prompt": "What is the start date of the contract?",
|
||||
"filters": "contains(path, `"Ide"`)"
|
||||
}'
|
||||
```
|
||||
|
||||
You can also retrieve the context documents that were used to answer the question,
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v2/answer' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"prompt": "What is the start date of the contract?",
|
||||
"return_context_docs": true
|
||||
}'
|
||||
```
|
||||
|
||||
- Note: You can limit the knowledge to a folder or, to only Word documents by using ```"contains(path, `docx`)"```
|
||||
- Note: You could also use a few filters separated with `||` (`or` clause) or with `&&` (`and` clause).
|
||||
|
||||
You can further modify behavior in the payload by defining keys and values in `-d '{key: value}'`.
|
||||
|
||||
If you wish to use another model, specify in the payload as `"model": "gpt-4"`.
|
||||
|
||||
For more detailed responses add `"response_type": "long"` to payload.
|
||||
|
||||
#### Summarization
|
||||
To summarize a list of texts, use the following `curl` command.
|
||||
|
||||
```bash
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:8000/v2/summarize' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"text_list": [
|
||||
"I love apples.",
|
||||
"I love oranges."
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Specifying the GPT model with `"model": "gpt-4"` is also possible.
|
||||
|
||||
This endpoint also supports setting different models in the query by default.
|
||||
|
||||
To execute similar curl queries as above, you can visit [ai-pipelines page](https://pathway.com/solutions/ai-pipelines/) and try out the queries from the Swagger UI.
|
||||
|
||||
|
||||
#### Adding Files to Index
|
||||
|
||||
First, you can try adding your files and seeing changes in the index. To test index updates, simply add more files to the `data` folder.
|
||||
|
||||
If you are using Google Drive or other sources, simply upload your files there.
|
||||
|
||||
### Using the UI
|
||||
This pipeline includes a simple ui written in Streamlit. After you run the pipeline with `docker compose up`, you can access the UI at `http://localhost:8501`. This UI uses the `/v2/answer` endpoint to answer your questions.
|
||||
@@ -0,0 +1,76 @@
|
||||
import logging
|
||||
from warnings import warn
|
||||
|
||||
import pathway as pw
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.question_answering import SummaryQuestionAnswerer
|
||||
from pathway.xpacks.llm.servers import QASummaryRestServer
|
||||
from pydantic import BaseModel, ConfigDict, InstanceOf
|
||||
|
||||
# To use advanced features with Pathway Live Data Framework Scale, get your free license key from
|
||||
# https://pathway.com/features and paste it below.
|
||||
# To use Pathway Live Data Framework Community, comment out the line below.
|
||||
pw.set_license_key("demo-license-key-with-telemetry")
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class App(BaseModel):
|
||||
question_answerer: InstanceOf[SummaryQuestionAnswerer]
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
with_cache: bool | None = None # deprecated
|
||||
persistence_backend: pw.persistence.Backend | None = None
|
||||
persistence_mode: pw.PersistenceMode | None = pw.PersistenceMode.UDF_CACHING
|
||||
terminate_on_error: bool = False
|
||||
|
||||
def run(self) -> None:
|
||||
server = QASummaryRestServer( # noqa: F841
|
||||
self.host, self.port, self.question_answerer
|
||||
)
|
||||
|
||||
if self.persistence_mode is None:
|
||||
if self.with_cache is True:
|
||||
warn(
|
||||
"`with_cache` is deprecated. Please use `persistence_mode` instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
persistence_mode = pw.PersistenceMode.UDF_CACHING
|
||||
else:
|
||||
persistence_mode = None
|
||||
else:
|
||||
persistence_mode = self.persistence_mode
|
||||
|
||||
if persistence_mode is not None:
|
||||
if self.persistence_backend is None:
|
||||
persistence_backend = pw.persistence.Backend.filesystem("./Cache")
|
||||
else:
|
||||
persistence_backend = self.persistence_backend
|
||||
persistence_config = pw.persistence.Config(
|
||||
persistence_backend,
|
||||
persistence_mode=persistence_mode,
|
||||
)
|
||||
else:
|
||||
persistence_config = None
|
||||
|
||||
pw.run(
|
||||
persistence_config=persistence_config,
|
||||
terminate_on_error=self.terminate_on_error,
|
||||
monitoring_level=pw.MonitoringLevel.NONE,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("app.yaml") as f:
|
||||
config = pw.load_yaml(f)
|
||||
app = App(**config)
|
||||
app.run()
|
||||
@@ -0,0 +1,118 @@
|
||||
# This YAML configuration file is used to set up and configure the Question Answering RAG template.
|
||||
# It defines various components such as data sources, language models, embedders, splitters, parsers, and retrievers.
|
||||
# Each section is configured to specify how the template should process and handle data for generating responses.
|
||||
# You can learn more about the YAML syntax here: https://pathway.com/developers/templates/configure-yaml
|
||||
|
||||
|
||||
|
||||
# $sources defines the data sources used to read the data which will be indexed in the RAG.
|
||||
# You can learn more how to configure data sources here:
|
||||
# https://pathway.com/developers/templates/yaml-examples/data-sources-examples
|
||||
|
||||
$sources:
|
||||
# File System connector, reading data locally.
|
||||
- !pw.io.fs.read
|
||||
path: data
|
||||
format: binary
|
||||
with_metadata: true
|
||||
|
||||
# Uncomment to use the SharePoint connector
|
||||
# - !pw.xpacks.connectors.sharepoint.read
|
||||
# url: $SHAREPOINT_URL
|
||||
# tenant: $SHAREPOINT_TENANT
|
||||
# client_id: $SHAREPOINT_CLIENT_ID
|
||||
# cert_path: sharepointcert.pem
|
||||
# thumbprint: $SHAREPOINT_THUMBPRINT
|
||||
# root_path: $SHAREPOINT_ROOT
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
# Uncomment to use the Google Drive connector
|
||||
# - !pw.io.gdrive.read
|
||||
# object_id: $DRIVE_ID
|
||||
# service_user_credentials_file: gdrive_indexer.json
|
||||
# file_name_pattern:
|
||||
# - "*.pdf"
|
||||
# - "*.pptx"
|
||||
# object_size_limit: null
|
||||
# with_metadata: true
|
||||
# refresh_interval: 30
|
||||
|
||||
|
||||
# Configures the LLM model settings for generating responses.
|
||||
# The list of available Pathway LLM wrappers is available here:
|
||||
# https://pathway.com/developers/api-docs/pathway-xpacks-llm/llms
|
||||
# You can learn more about those in our documentation:
|
||||
# https://pathway.com/developers/templates/rag-customization/llm-chats
|
||||
|
||||
$llm: !pw.xpacks.llm.llms.OpenAIChat
|
||||
model: "gpt-4.1-mini"
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy
|
||||
max_retries: 6
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
temperature: 0
|
||||
capacity: 8
|
||||
async_mode: "fully_async"
|
||||
|
||||
# Specifies the embedder model for converting text into embeddings.
|
||||
$embedder: !pw.xpacks.llm.embedders.OpenAIEmbedder
|
||||
model: "text-embedding-3-small"
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
retry_strategy: !pw.udfs.ExponentialBackoffRetryStrategy {}
|
||||
|
||||
# Defines the splitter settings for dividing text into smaller chunks.
|
||||
$splitter: !pw.xpacks.llm.splitters.TokenCountSplitter
|
||||
max_tokens: 400
|
||||
|
||||
# Configures the parser for processing and extracting information from documents.
|
||||
$parser: !pw.xpacks.llm.parsers.DoclingParser
|
||||
table_parsing_strategy: "llm"
|
||||
async_mode: "fully_async"
|
||||
chunk: false
|
||||
cache_strategy: !pw.udfs.DefaultCache {}
|
||||
|
||||
# Sets up the retriever factory for indexing and retrieving documents.
|
||||
$retriever_factory: !pw.indexing.UsearchKnnFactory
|
||||
reserved_space: 1000
|
||||
embedder: $embedder
|
||||
metric: !pw.indexing.USearchMetricKind.COS
|
||||
|
||||
# Manages the storage and retrieval of documents for the RAG template.
|
||||
$document_store: !pw.xpacks.llm.document_store.DocumentStore
|
||||
docs: $sources
|
||||
parser: $parser
|
||||
splitter: $splitter
|
||||
retriever_factory: $retriever_factory
|
||||
|
||||
# Configures the question-answering component using the RAG approach.
|
||||
# The component builds a RAG over an index.
|
||||
# You can interact with obtained RAG using a REST API.
|
||||
# You can learn more about the available operations here:
|
||||
# https://pathway.com/developers/templates/rag-customization/rest-api
|
||||
question_answerer: !pw.xpacks.llm.question_answering.BaseRAGQuestionAnswerer
|
||||
llm: $llm
|
||||
indexer: $document_store
|
||||
# You can set the number of documents to be included as the context of the query
|
||||
# search_topk: 6
|
||||
# You can use your own prompt for querying.
|
||||
# For that set prompt_template to string with `{query}` used as a placeholder for the question,
|
||||
# and `{context}` as a placeholder for context documents.
|
||||
# prompt_template: "Given these documents: {context}, please answer the question: {query}"
|
||||
|
||||
# Change host and port of the webserver by uncommenting these lines
|
||||
# host: "0.0.0.0"
|
||||
# port: $PATHWAY_PORT
|
||||
|
||||
# By default, caching is enabled for UDFs with cache_strategy set.
|
||||
# You can disable it by uncommenting the following line.
|
||||
# persistence_mode: null
|
||||
# You can also set persistence_mode to !pw.PersistenceMode.PERSISTING to enable persistence
|
||||
# across restarts.
|
||||
# By default, when enabled, Cache is stored in .Cache directory.
|
||||
# You can customize the location by uncommenting and modifying the following lines:
|
||||
# persistence_backend: !pw.persistence.Backend.filesystem
|
||||
# path: ".Cache"
|
||||
|
||||
# If `terminate_on_error` is true then the program will terminate whenever any error is encountered.
|
||||
# Defaults to false, uncomment the following line if you want to set it to true
|
||||
# terminate_on_error: true
|
||||
@@ -0,0 +1,34 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
PATHWAY_SRC_IMAGE: ${PATHWAY_SRC_IMAGE:-pathwaycom/pathway:latest}
|
||||
ports:
|
||||
- "${PATHWAY_PORT:-8000}:${PATHWAY_PORT:-8000}"
|
||||
networks:
|
||||
- network
|
||||
environment:
|
||||
PATHWAY_PORT: "${PATHWAY_PORT:-8000}"
|
||||
PATHWAY_LICENSE_KEY: $PATHWAY_LICENSE_KEY
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./Cache:/app/Cache
|
||||
|
||||
ui:
|
||||
build:
|
||||
context: ui
|
||||
args:
|
||||
PATHWAY_SRC_IMAGE: ${PATHWAY_SRC_IMAGE:-pathwaycom/pathway:latest}
|
||||
networks:
|
||||
- network
|
||||
environment:
|
||||
PATHWAY_HOST: "app"
|
||||
PATHWAY_PORT: "${PATHWAY_PORT:-8000}"
|
||||
UI_PORT: 8501
|
||||
ports:
|
||||
- "8501:8501"
|
||||
|
||||
networks:
|
||||
network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,3 @@
|
||||
pathway[all]
|
||||
python-dotenv~=1.0
|
||||
mpmath~=1.3
|
||||
@@ -0,0 +1,8 @@
|
||||
[client]
|
||||
toolbarMode = "minimal"
|
||||
|
||||
[server]
|
||||
enableStaticServing = true
|
||||
|
||||
[theme]
|
||||
base = "light"
|
||||
@@ -0,0 +1,14 @@
|
||||
ARG PATHWAY_SRC_IMAGE=pathwaycom/pathway:latest
|
||||
|
||||
FROM ${PATHWAY_SRC_IMAGE}
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /ui
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -U --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD exec streamlit run ui.py --server.port ${UI_PORT}
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,2 @@
|
||||
streamlit==1.37.0
|
||||
load_dotenv==0.1.0
|
||||
|
After Width: | Height: | Size: 287 KiB |
@@ -0,0 +1,209 @@
|
||||
# Copyright © 2026 Pathway
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import streamlit as st
|
||||
from dotenv import load_dotenv
|
||||
from pathway.xpacks.llm.document_store import IndexingStatus
|
||||
from pathway.xpacks.llm.question_answering import RAGClient
|
||||
|
||||
load_dotenv()
|
||||
|
||||
PATHWAY_HOST = os.environ.get("PATHWAY_HOST", "app")
|
||||
PATHWAY_PORT = os.environ.get("PATHWAY_PORT", 8000)
|
||||
|
||||
st.set_page_config(
|
||||
page_title="Pathway Live Data Framework RAG App", page_icon="favicon.ico"
|
||||
)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
force=True,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("streamlit")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
conn = RAGClient(url=f"http://{PATHWAY_HOST}:{PATHWAY_PORT}")
|
||||
|
||||
note = """
|
||||
<H4><b>Ask a question"""
|
||||
st.markdown(note, unsafe_allow_html=True)
|
||||
|
||||
st.markdown(
|
||||
"""
|
||||
<style>
|
||||
div[data-baseweb="base-input"]{
|
||||
}
|
||||
input[class]{
|
||||
font-size:150%;
|
||||
color: black;}
|
||||
button[data-testid="baseButton-primary"], button[data-testid="baseButton-secondary"]{
|
||||
border: none;
|
||||
display: flex;
|
||||
background-color: #E7E7E7;
|
||||
color: #454545;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
button[data-testid="baseButton-primary"]:hover{
|
||||
color: #1C1CF0;
|
||||
background-color: rgba(28,28,240,0.3);
|
||||
}
|
||||
button[data-testid="baseButton-secondary"]:hover{
|
||||
color: #DC280B;
|
||||
background-color: rgba(220,40,11,0.3);
|
||||
}
|
||||
div[data-testid="stHorizontalBlock"]:has(button[data-testid="baseButton-primary"]){
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 0;
|
||||
width: 3rem;
|
||||
|
||||
transform: translateY(-500px) translateX(672px);
|
||||
}
|
||||
</style>
|
||||
""",
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
|
||||
|
||||
question = st.text_input(label="", placeholder="Ask your question?")
|
||||
|
||||
|
||||
def get_indexed_files(metadata_list: list[dict], opt_key: str) -> list:
|
||||
"""Get all available options in a specific metadata key."""
|
||||
only_indexed_files = [
|
||||
file
|
||||
for file in metadata_list
|
||||
if file["_indexing_status"] == IndexingStatus.INDEXED
|
||||
]
|
||||
options = set(map(lambda x: x[opt_key], only_indexed_files))
|
||||
return list(options)
|
||||
|
||||
|
||||
def get_ingested_files(metadata_list: list[dict], opt_key: str) -> list:
|
||||
"""Get all available options in a specific metadata key."""
|
||||
not_indexed_files = [
|
||||
file
|
||||
for file in metadata_list
|
||||
if file["_indexing_status"] == IndexingStatus.INGESTED
|
||||
]
|
||||
options = set(map(lambda x: x[opt_key], not_indexed_files))
|
||||
return list(options)
|
||||
|
||||
|
||||
logger.info("Requesting list_documents...")
|
||||
document_meta_list = conn.list_documents(keys=[])
|
||||
logger.info("Received response list_documents")
|
||||
|
||||
st.session_state["document_meta_list"] = document_meta_list
|
||||
|
||||
indexed_files = get_indexed_files(st.session_state["document_meta_list"], "path")
|
||||
ingested_files = get_ingested_files(st.session_state["document_meta_list"], "path")
|
||||
|
||||
|
||||
logo_htm = """
|
||||
<div>
|
||||
<figure style="display: flex; align-items: center; margin: 0;">
|
||||
<img style="max-width:300px" src="app/static/pathway-logo-black.png" alt="Pathway Logo">
|
||||
</figure>
|
||||
</div>
|
||||
"""
|
||||
|
||||
with st.sidebar:
|
||||
st.markdown(logo_htm, unsafe_allow_html=True)
|
||||
|
||||
st.info(
|
||||
body="See the source code [here](https://github.com/pathwaycom/llm-app/tree/main/templates/question_answering_rag).", # noqa: E501
|
||||
icon=":material/code:",
|
||||
)
|
||||
|
||||
indexed_file_names = [i.split("/")[-1] for i in indexed_files]
|
||||
ingested_file_names = [i.split("/")[-1] for i in ingested_files]
|
||||
|
||||
markdown_table = "| Indexed files |\n| --- |\n"
|
||||
for file_name in indexed_file_names:
|
||||
markdown_table += f"| {file_name} |\n"
|
||||
|
||||
if len(ingested_file_names) > 0:
|
||||
markdown_table += "| Files being processed |\n| --- |\n"
|
||||
for file_name in ingested_file_names:
|
||||
markdown_table += f"| {file_name} |\n"
|
||||
|
||||
st.markdown(markdown_table, unsafe_allow_html=True)
|
||||
|
||||
st.button("⟳ Refresh", use_container_width=True)
|
||||
|
||||
css = """
|
||||
<style>
|
||||
.slider-container {
|
||||
margin-top: 20px; /* Add some space between the main image and the slider */
|
||||
}
|
||||
|
||||
.slider-item {
|
||||
float: left;
|
||||
margin: 10px;
|
||||
width: 120px; /* Adjust the width to your liking */
|
||||
// height: 50px; /* Adjust the height to your liking */
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider-item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.slider-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.slider-item {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
</style>"""
|
||||
|
||||
|
||||
st.markdown(css, unsafe_allow_html=True)
|
||||
|
||||
|
||||
if question:
|
||||
logger.info(
|
||||
{
|
||||
"_type": "search_request_event",
|
||||
"query": question,
|
||||
}
|
||||
)
|
||||
|
||||
with st.spinner("Retrieving response..."):
|
||||
api_response = conn.answer(question, return_context_docs=True)
|
||||
response = api_response["response"]
|
||||
context_docs = api_response["context_docs"]
|
||||
|
||||
logger.info(
|
||||
{
|
||||
"_type": "search_response_event",
|
||||
"query": question,
|
||||
"response": type(response),
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(type(response))
|
||||
|
||||
st.markdown(f"**Answering question:** {question}")
|
||||
st.markdown(f"""{response}""")
|
||||
with st.expander(label="Context documents"):
|
||||
st.markdown("Documents sent to LLM as context:\n")
|
||||
for i, doc in enumerate(context_docs):
|
||||
st.markdown(
|
||||
f"{i+1}. Path: {doc['metadata']['path']}\n ```\n{doc['text']}\n```"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
Cache
|
||||