merge
This commit is contained in:
commit
25e0c6cbd1
22 changed files with 888 additions and 152 deletions
77
.github/workflows/build-multiarch.yml
vendored
77
.github/workflows/build-multiarch.yml
vendored
|
|
@ -53,58 +53,63 @@ jobs:
|
|||
# backend
|
||||
- image: backend
|
||||
file: ./Dockerfile.backend
|
||||
tag: phact/openrag-backend
|
||||
tag: langflowai/openrag-backend
|
||||
platform: linux/amd64
|
||||
arch: amd64
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
- image: backend
|
||||
file: ./Dockerfile.backend
|
||||
tag: phact/openrag-backend
|
||||
tag: langflowai/openrag-backend
|
||||
platform: linux/arm64
|
||||
arch: arm64
|
||||
runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
#runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
runs-on: RagRunner
|
||||
|
||||
# frontend
|
||||
- image: frontend
|
||||
file: ./Dockerfile.frontend
|
||||
tag: phact/openrag-frontend
|
||||
tag: langflowai/openrag-frontend
|
||||
platform: linux/amd64
|
||||
arch: amd64
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
- image: frontend
|
||||
file: ./Dockerfile.frontend
|
||||
tag: phact/openrag-frontend
|
||||
tag: langflowai/openrag-frontend
|
||||
platform: linux/arm64
|
||||
arch: arm64
|
||||
runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
#runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
runs-on: RagRunner
|
||||
|
||||
# langflow
|
||||
- image: langflow
|
||||
file: ./Dockerfile.langflow
|
||||
tag: phact/openrag-langflow
|
||||
tag: langflowai/openrag-langflow
|
||||
platform: linux/amd64
|
||||
arch: amd64
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
- image: langflow
|
||||
file: ./Dockerfile.langflow
|
||||
tag: phact/openrag-langflow
|
||||
tag: langflowai/openrag-langflow
|
||||
platform: linux/arm64
|
||||
arch: arm64
|
||||
runs-on: self-hosted
|
||||
#runs-on: self-hosted
|
||||
runs-on: RagRunner
|
||||
|
||||
# opensearch
|
||||
- image: opensearch
|
||||
file: ./Dockerfile
|
||||
tag: phact/openrag-opensearch
|
||||
tag: langflowai/openrag-opensearch
|
||||
platform: linux/amd64
|
||||
arch: amd64
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
- image: opensearch
|
||||
file: ./Dockerfile
|
||||
tag: phact/openrag-opensearch
|
||||
tag: langflowai/openrag-opensearch
|
||||
platform: linux/arm64
|
||||
arch: arm64
|
||||
runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
#runs-on: [self-hosted, linux, ARM64, langflow-ai-arm64-2]
|
||||
#runs-on: self-hosted
|
||||
runs-on: RagRunner
|
||||
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
|
|
@ -165,40 +170,40 @@ jobs:
|
|||
VERSION=${{ steps.version.outputs.version }}
|
||||
|
||||
# Create versioned tags
|
||||
docker buildx imagetools create -t phact/openrag-backend:$VERSION \
|
||||
phact/openrag-backend:$VERSION-amd64 \
|
||||
phact/openrag-backend:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-backend:$VERSION \
|
||||
langflowai/openrag-backend:$VERSION-amd64 \
|
||||
langflowai/openrag-backend:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-frontend:$VERSION \
|
||||
phact/openrag-frontend:$VERSION-amd64 \
|
||||
phact/openrag-frontend:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-frontend:$VERSION \
|
||||
langflowai/openrag-frontend:$VERSION-amd64 \
|
||||
langflowai/openrag-frontend:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-langflow:$VERSION \
|
||||
phact/openrag-langflow:$VERSION-amd64 \
|
||||
phact/openrag-langflow:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-langflow:$VERSION \
|
||||
langflowai/openrag-langflow:$VERSION-amd64 \
|
||||
langflowai/openrag-langflow:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-opensearch:$VERSION \
|
||||
phact/openrag-opensearch:$VERSION-amd64 \
|
||||
phact/openrag-opensearch:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-opensearch:$VERSION \
|
||||
langflowai/openrag-opensearch:$VERSION-amd64 \
|
||||
langflowai/openrag-opensearch:$VERSION-arm64
|
||||
|
||||
# Only update latest tags if version is numeric
|
||||
if [[ "$VERSION" =~ ^[0-9.-]+$ ]]; then
|
||||
echo "Updating latest tags for production release: $VERSION"
|
||||
docker buildx imagetools create -t phact/openrag-backend:latest \
|
||||
phact/openrag-backend:$VERSION-amd64 \
|
||||
phact/openrag-backend:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-backend:latest \
|
||||
langflowai/openrag-backend:$VERSION-amd64 \
|
||||
langflowai/openrag-backend:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-frontend:latest \
|
||||
phact/openrag-frontend:$VERSION-amd64 \
|
||||
phact/openrag-frontend:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-frontend:latest \
|
||||
langflowai/openrag-frontend:$VERSION-amd64 \
|
||||
langflowai/openrag-frontend:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-langflow:latest \
|
||||
phact/openrag-langflow:$VERSION-amd64 \
|
||||
phact/openrag-langflow:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-langflow:latest \
|
||||
langflowai/openrag-langflow:$VERSION-amd64 \
|
||||
langflowai/openrag-langflow:$VERSION-arm64
|
||||
|
||||
docker buildx imagetools create -t phact/openrag-opensearch:latest \
|
||||
phact/openrag-opensearch:$VERSION-amd64 \
|
||||
phact/openrag-opensearch:$VERSION-arm64
|
||||
docker buildx imagetools create -t langflowai/openrag-opensearch:latest \
|
||||
langflowai/openrag-opensearch:$VERSION-amd64 \
|
||||
langflowai/openrag-opensearch:$VERSION-arm64
|
||||
else
|
||||
echo "Skipping latest tags - version: $VERSION (not numeric)"
|
||||
fi
|
||||
|
|
|
|||
18
.github/workflows/test-integration.yml
vendored
18
.github/workflows/test-integration.yml
vendored
|
|
@ -31,14 +31,22 @@ jobs:
|
|||
|
||||
steps:
|
||||
- run: df -h
|
||||
#- name: "node-cleanup"
|
||||
#run: |
|
||||
# sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
|
||||
# sudo docker image prune --all --force
|
||||
# sudo docker builder prune -a
|
||||
|
||||
- name: Cleanup Docker cache
|
||||
run: |
|
||||
docker system prune -af || true
|
||||
docker builder prune -af || true
|
||||
|
||||
- run: df -h
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Verify workspace
|
||||
run: |
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Workspace: ${GITHUB_WORKSPACE}"
|
||||
ls -la
|
||||
|
||||
- name: Set up UV
|
||||
uses: astral-sh/setup-uv@v3
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM langflowai/langflow-nightly:1.7.0.dev5
|
||||
FROM langflowai/langflow-nightly:1.7.0.dev19
|
||||
|
||||
EXPOSE 7860
|
||||
|
||||
|
|
|
|||
10
Makefile
10
Makefile
|
|
@ -210,7 +210,7 @@ test-ci:
|
|||
echo "Pulling latest images..."; \
|
||||
docker compose -f docker-compose-cpu.yml pull; \
|
||||
echo "Building OpenSearch image override..."; \
|
||||
docker build --no-cache -t phact/openrag-opensearch:latest -f Dockerfile .; \
|
||||
docker build --no-cache -t langflowai/openrag-opensearch:latest -f Dockerfile .; \
|
||||
echo "Starting infra (OpenSearch + Dashboards + Langflow) with CPU containers"; \
|
||||
docker compose -f docker-compose-cpu.yml up -d opensearch dashboards langflow; \
|
||||
echo "Starting docling-serve..."; \
|
||||
|
|
@ -288,10 +288,10 @@ test-ci-local:
|
|||
echo "Cleaning up old containers and volumes..."; \
|
||||
docker compose -f docker-compose-cpu.yml down -v 2>/dev/null || true; \
|
||||
echo "Building all images locally..."; \
|
||||
docker build -t phact/openrag-opensearch:latest -f Dockerfile .; \
|
||||
docker build -t phact/openrag-backend:latest -f Dockerfile.backend .; \
|
||||
docker build -t phact/openrag-frontend:latest -f Dockerfile.frontend .; \
|
||||
docker build -t phact/openrag-langflow:latest -f Dockerfile.langflow .; \
|
||||
docker build -t langflowai/openrag-opensearch:latest -f Dockerfile .; \
|
||||
docker build -t langflowai/openrag-backend:latest -f Dockerfile.backend .; \
|
||||
docker build -t langflowai/openrag-frontend:latest -f Dockerfile.frontend .; \
|
||||
docker build -t langflowai/openrag-langflow:latest -f Dockerfile.langflow .; \
|
||||
echo "Starting infra (OpenSearch + Dashboards + Langflow) with CPU containers"; \
|
||||
docker compose -f docker-compose-cpu.yml up -d opensearch dashboards langflow; \
|
||||
echo "Starting docling-serve..."; \
|
||||
|
|
|
|||
42
README.md
42
README.md
|
|
@ -18,27 +18,29 @@ OpenRAG is a comprehensive Retrieval-Augmented Generation platform that enables
|
|||
</div>
|
||||
<div align="center">
|
||||
<a href="#quickstart" style="color: #0366d6;">Quickstart</a> |
|
||||
<a href="#tui-interface" style="color: #0366d6;">TUI Interface</a> |
|
||||
<a href="#docker-deployment" style="color: #0366d6;">Docker Deployment</a> |
|
||||
<a href="#install-python-package" style="color: #0366d6;">Python package</a> |
|
||||
<a href="#docker-or-podman-installation" style="color: #0366d6;">Docker or Podman</a> |
|
||||
<a href="#development" style="color: #0366d6;">Development</a> |
|
||||
<a href="#troubleshooting" style="color: #0366d6;">Troubleshooting</a>
|
||||
</div>
|
||||
|
||||
## Quickstart
|
||||
|
||||
To quickly run OpenRAG without creating or modifying any project files, use `uvx`:
|
||||
To run OpenRAG without creating or modifying any project files, use `uvx`:
|
||||
|
||||
```bash
|
||||
uvx openrag
|
||||
```
|
||||
This runs OpenRAG without installing it to your project or globally.
|
||||
To run a specific version of OpenRAG, add the version to the command, such as: `uvx --from openrag==0.1.25 openrag`.
|
||||
|
||||
This command runs OpenRAG without installing it to your project or globally.
|
||||
|
||||
To run a specific version of OpenRAG, run `uvx --from openrag==VERSION openrag`.
|
||||
|
||||
## Install Python package
|
||||
|
||||
To first set up a project and then install the OpenRAG Python package, do the following:
|
||||
To add the OpenRAG Python package to a Python project, use `uv`:
|
||||
|
||||
1. Create a new project with a virtual environment using `uv init`.
|
||||
1. Create a new project with a virtual environment using `uv init`:
|
||||
|
||||
```bash
|
||||
uv init YOUR_PROJECT_NAME
|
||||
|
|
@ -48,33 +50,33 @@ To first set up a project and then install the OpenRAG Python package, do the fo
|
|||
The `(venv)` prompt doesn't change, but `uv` commands will automatically use the project's virtual environment.
|
||||
For more information on virtual environments, see the [uv documentation](https://docs.astral.sh/uv/pip/environments).
|
||||
|
||||
2. Add OpenRAG to your project.
|
||||
2. Add OpenRAG to your project:
|
||||
|
||||
```bash
|
||||
uv add openrag
|
||||
```
|
||||
|
||||
To add a specific version of OpenRAG:
|
||||
```bash
|
||||
uv add openrag==0.1.25
|
||||
```
|
||||
To add a specific version of OpenRAG, run `uv add openrag==VERSION`.
|
||||
|
||||
3. Start the OpenRAG terminal user interface (TUI):
|
||||
|
||||
3. Start the OpenRAG TUI.
|
||||
```bash
|
||||
uv run openrag
|
||||
```
|
||||
|
||||
4. Continue with the [Quickstart](https://docs.openr.ag/quickstart).
|
||||
|
||||
For the full TUI installation guide, see [TUI](https://docs.openr.ag/install).
|
||||
For all installation options, see the [OpenRAG installation guide](https://docs.openr.ag/install).
|
||||
|
||||
## Docker or Podman installation
|
||||
|
||||
For more information, see [Install OpenRAG containers](https://docs.openr.ag/docker).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For common issues and fixes, see [Troubleshoot](https://docs.openr.ag/support/troubleshoot).
|
||||
By default, OpenRAG automatically starts the required containers and helps you manage them.
|
||||
To install OpenRAG with self-managed containers, see the [OpenRAG installation guide](https://docs.openr.ag/docker).
|
||||
|
||||
## Development
|
||||
|
||||
For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
For developers wanting to contribute to OpenRAG or set up a development environment, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For common issues and fixes, see [Troubleshoot OpenRAG](https://docs.openr.ag/support/troubleshoot).
|
||||
141
docker-compose-cpu.yml
Normal file
141
docker-compose-cpu.yml
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
services:
|
||||
opensearch:
|
||||
image: langflowai/openrag-opensearch:${OPENRAG_VERSION:-latest}
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
container_name: os
|
||||
depends_on:
|
||||
- openrag-backend
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_PASSWORD}
|
||||
# Run security setup in background after OpenSearch starts
|
||||
command: >
|
||||
bash -c "
|
||||
# Ensure data directory has correct permissions
|
||||
sudo chown -R opensearch:opensearch /usr/share/opensearch/data || true
|
||||
|
||||
# Start OpenSearch in background
|
||||
/usr/share/opensearch/opensearch-docker-entrypoint.sh opensearch &
|
||||
|
||||
# Wait a bit for OpenSearch to start, then apply security config
|
||||
sleep 10 && /usr/share/opensearch/setup-security.sh &
|
||||
|
||||
# Wait for background processes
|
||||
wait
|
||||
"
|
||||
ports:
|
||||
- "9200:9200"
|
||||
- "9600:9600"
|
||||
volumes:
|
||||
- ${OPENSEARCH_DATA_PATH:-./opensearch-data}:/usr/share/opensearch/data:Z
|
||||
|
||||
dashboards:
|
||||
image: opensearchproject/opensearch-dashboards:3.0.0
|
||||
container_name: osdash
|
||||
depends_on:
|
||||
- opensearch
|
||||
environment:
|
||||
OPENSEARCH_HOSTS: '["https://opensearch:9200"]'
|
||||
OPENSEARCH_USERNAME: "admin"
|
||||
OPENSEARCH_PASSWORD: ${OPENSEARCH_PASSWORD}
|
||||
ports:
|
||||
- "5601:5601"
|
||||
|
||||
openrag-backend:
|
||||
image: langflowai/openrag-backend:${OPENRAG_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.backend
|
||||
container_name: openrag-backend
|
||||
depends_on:
|
||||
- langflow
|
||||
environment:
|
||||
- OPENSEARCH_HOST=opensearch
|
||||
- LANGFLOW_URL=http://langflow:7860
|
||||
- LANGFLOW_PUBLIC_URL=${LANGFLOW_PUBLIC_URL}
|
||||
- LANGFLOW_AUTO_LOGIN=${LANGFLOW_AUTO_LOGIN}
|
||||
- LANGFLOW_SECRET_KEY=${LANGFLOW_SECRET_KEY}
|
||||
- LANGFLOW_SUPERUSER=${LANGFLOW_SUPERUSER}
|
||||
- LANGFLOW_SUPERUSER_PASSWORD=${LANGFLOW_SUPERUSER_PASSWORD}
|
||||
- LANGFLOW_CHAT_FLOW_ID=${LANGFLOW_CHAT_FLOW_ID}
|
||||
- LANGFLOW_INGEST_FLOW_ID=${LANGFLOW_INGEST_FLOW_ID}
|
||||
- LANGFLOW_URL_INGEST_FLOW_ID=${LANGFLOW_URL_INGEST_FLOW_ID}
|
||||
- DISABLE_INGEST_WITH_LANGFLOW=${DISABLE_INGEST_WITH_LANGFLOW:-false}
|
||||
- NUDGES_FLOW_ID=${NUDGES_FLOW_ID}
|
||||
- OPENSEARCH_PORT=9200
|
||||
- OPENSEARCH_USERNAME=admin
|
||||
- OPENSEARCH_PASSWORD=${OPENSEARCH_PASSWORD}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||
- WATSONX_API_KEY=${WATSONX_API_KEY}
|
||||
- WATSONX_ENDPOINT=${WATSONX_ENDPOINT}
|
||||
- WATSONX_PROJECT_ID=${WATSONX_PROJECT_ID}
|
||||
- OLLAMA_ENDPOINT=${OLLAMA_ENDPOINT}
|
||||
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID}
|
||||
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET}
|
||||
- MICROSOFT_GRAPH_OAUTH_CLIENT_ID=${MICROSOFT_GRAPH_OAUTH_CLIENT_ID}
|
||||
- MICROSOFT_GRAPH_OAUTH_CLIENT_SECRET=${MICROSOFT_GRAPH_OAUTH_CLIENT_SECRET}
|
||||
- WEBHOOK_BASE_URL=${WEBHOOK_BASE_URL}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
|
||||
volumes:
|
||||
- ./documents:/app/documents:Z
|
||||
- ./keys:/app/keys:Z
|
||||
- ./flows:/app/flows:U,z
|
||||
|
||||
openrag-frontend:
|
||||
image: langflowai/openrag-frontend:${OPENRAG_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.frontend
|
||||
container_name: openrag-frontend
|
||||
depends_on:
|
||||
- openrag-backend
|
||||
environment:
|
||||
- OPENRAG_BACKEND_HOST=openrag-backend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
langflow:
|
||||
volumes:
|
||||
- ./flows:/app/flows:U,z
|
||||
image: langflowai/openrag-langflow:${LANGFLOW_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.langflow
|
||||
container_name: langflow
|
||||
ports:
|
||||
- "7860:7860"
|
||||
environment:
|
||||
- LANGFLOW_DEACTIVATE_TRACING=true
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||
- WATSONX_API_KEY=${WATSONX_API_KEY}
|
||||
- WATSONX_ENDPOINT=${WATSONX_ENDPOINT}
|
||||
- WATSONX_PROJECT_ID=${WATSONX_PROJECT_ID}
|
||||
- OLLAMA_BASE_URL=${OLLAMA_ENDPOINT}
|
||||
- LANGFLOW_LOAD_FLOWS_PATH=/app/flows
|
||||
- LANGFLOW_SECRET_KEY=${LANGFLOW_SECRET_KEY}
|
||||
- JWT=None
|
||||
- OWNER=None
|
||||
- OWNER_NAME=None
|
||||
- OWNER_EMAIL=None
|
||||
- CONNECTOR_TYPE=system
|
||||
- CONNECTOR_TYPE_URL=url
|
||||
- OPENRAG-QUERY-FILTER="{}"
|
||||
- OPENSEARCH_PASSWORD=${OPENSEARCH_PASSWORD}
|
||||
- FILENAME=None
|
||||
- MIMETYPE=None
|
||||
- FILESIZE=0
|
||||
- LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT=JWT,OPENRAG-QUERY-FILTER,OPENSEARCH_PASSWORD,OWNER,OWNER_NAME,OWNER_EMAIL,CONNECTOR_TYPE,FILENAME,MIMETYPE,FILESIZE
|
||||
- LANGFLOW_LOG_LEVEL=DEBUG
|
||||
- LANGFLOW_AUTO_LOGIN=${LANGFLOW_AUTO_LOGIN}
|
||||
- LANGFLOW_SUPERUSER=${LANGFLOW_SUPERUSER}
|
||||
- LANGFLOW_SUPERUSER_PASSWORD=${LANGFLOW_SUPERUSER_PASSWORD}
|
||||
- LANGFLOW_NEW_USER_IS_ACTIVE=${LANGFLOW_NEW_USER_IS_ACTIVE}
|
||||
- LANGFLOW_ENABLE_SUPERUSER_CLI=${LANGFLOW_ENABLE_SUPERUSER_CLI}
|
||||
# - DEFAULT_FOLDER_NAME=OpenRAG
|
||||
- HIDE_GETTING_STARTED_PROGRESS=true
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
opensearch:
|
||||
image: phact/openrag-opensearch:${OPENRAG_VERSION:-latest}
|
||||
image: langflowai/openrag-opensearch:${OPENRAG_VERSION:-latest}
|
||||
#build:
|
||||
#context: .
|
||||
#dockerfile: Dockerfile
|
||||
|
|
@ -44,7 +44,7 @@ services:
|
|||
- "5601:5601"
|
||||
|
||||
openrag-backend:
|
||||
image: phact/openrag-backend:${OPENRAG_VERSION:-latest}
|
||||
image: langflowai/openrag-backend:${OPENRAG_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.backend
|
||||
|
|
@ -86,7 +86,7 @@ services:
|
|||
- ./flows:/app/flows:U,z
|
||||
|
||||
openrag-frontend:
|
||||
image: phact/openrag-frontend:${OPENRAG_VERSION:-latest}
|
||||
image: langflowai/openrag-frontend:${OPENRAG_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.frontend
|
||||
|
|
@ -101,7 +101,7 @@ services:
|
|||
langflow:
|
||||
volumes:
|
||||
- ./flows:/app/flows:U,z
|
||||
image: phact/openrag-langflow:${LANGFLOW_VERSION:-latest}
|
||||
image: langflowai/openrag-langflow:${LANGFLOW_VERSION:-latest}
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile.langflow
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ OpenRAG has two Docker Compose files. Both files deploy the same applications an
|
|||
|
||||
- Install the following:
|
||||
|
||||
- [Python](https://www.python.org/downloads/release/python-3100/) version 3.10 to 3.13.
|
||||
- [Python](https://www.python.org/downloads/release/python-3100/) version 3.13 or later.
|
||||
- [uv](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
- [Podman](https://podman.io/docs/installation) (recommended) or [Docker](https://docs.docker.com/get-docker/).
|
||||
- [`podman-compose`](https://docs.podman.io/en/latest/markdown/podman-compose.1.html) or [Docker Compose](https://docs.docker.com/compose/install/). To use Docker Compose with Podman, you must alias Docker Compose commands to Podman commands.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ If you prefer running Podman or Docker containers and manually editing `.env` fi
|
|||
|
||||
## Prerequisites
|
||||
|
||||
- All OpenRAG installations require [Python](https://www.python.org/downloads/release/python-3100/) version 3.10 to 3.13.
|
||||
- All OpenRAG installations require [Python](https://www.python.org/downloads/release/python-3100/) version 3.13 or later.
|
||||
|
||||
- If you aren't using the automatic installer script, install the following:
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ This quickstart requires the following:
|
|||
This quickstart uses OpenAI for simplicity.
|
||||
For other providers, see the complete [installation guide](/install).
|
||||
|
||||
- [Python](https://www.python.org/downloads/release/python-3100/) version 3.10 to 3.13.
|
||||
- [Python](https://www.python.org/downloads/release/python-3100/) version 3.13 or later.
|
||||
|
||||
- Microsoft Windows only: To run OpenRAG on Windows, you must use the Windows Subsystem for Linux (WSL).
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -31,6 +31,11 @@ import {
|
|||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { StatusBadge } from "@/components/ui/status-badge";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import {
|
||||
DeleteConfirmationDialog,
|
||||
formatFilesToDelete,
|
||||
|
|
@ -156,9 +161,16 @@ function SearchPage() {
|
|||
}}
|
||||
>
|
||||
{getSourceIcon(data?.connector_type)}
|
||||
<span className="font-medium text-foreground truncate">
|
||||
{value}
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="font-medium text-foreground truncate">
|
||||
{value}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" align="start">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "openrag"
|
||||
version = "0.1.38"
|
||||
version = "0.1.40"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ class ServiceInfo:
|
|||
image: Optional[str] = None
|
||||
image_digest: Optional[str] = None
|
||||
created: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.ports is None:
|
||||
|
|
@ -135,6 +136,96 @@ class ContainerManager:
|
|||
return self.platform_detector.get_compose_installation_instructions()
|
||||
return self.platform_detector.get_installation_instructions()
|
||||
|
||||
def _extract_ports_from_compose(self) -> Dict[str, List[int]]:
|
||||
"""Extract port mappings from compose files.
|
||||
|
||||
Returns:
|
||||
Dict mapping service name to list of host ports
|
||||
"""
|
||||
service_ports: Dict[str, List[int]] = {}
|
||||
|
||||
compose_files = [self.compose_file]
|
||||
if hasattr(self, 'cpu_compose_file') and self.cpu_compose_file and self.cpu_compose_file.exists():
|
||||
compose_files.append(self.cpu_compose_file)
|
||||
|
||||
for compose_file in compose_files:
|
||||
if not compose_file.exists():
|
||||
continue
|
||||
|
||||
try:
|
||||
import re
|
||||
content = compose_file.read_text()
|
||||
current_service = None
|
||||
in_ports_section = False
|
||||
|
||||
for line in content.splitlines():
|
||||
# Detect service names
|
||||
service_match = re.match(r'^ (\w[\w-]*):$', line)
|
||||
if service_match:
|
||||
current_service = service_match.group(1)
|
||||
in_ports_section = False
|
||||
if current_service not in service_ports:
|
||||
service_ports[current_service] = []
|
||||
continue
|
||||
|
||||
# Detect ports section
|
||||
if current_service and re.match(r'^ ports:$', line):
|
||||
in_ports_section = True
|
||||
continue
|
||||
|
||||
# Exit ports section on new top-level key
|
||||
if in_ports_section and re.match(r'^ \w+:', line):
|
||||
in_ports_section = False
|
||||
|
||||
# Extract port mappings
|
||||
if in_ports_section and current_service:
|
||||
# Match patterns like: - "3000:3000", - "9200:9200", - 7860:7860
|
||||
port_match = re.search(r'["\']?(\d+):\d+["\']?', line)
|
||||
if port_match:
|
||||
host_port = int(port_match.group(1))
|
||||
if host_port not in service_ports[current_service]:
|
||||
service_ports[current_service].append(host_port)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Error parsing {compose_file} for ports: {e}")
|
||||
continue
|
||||
|
||||
return service_ports
|
||||
|
||||
async def check_ports_available(self) -> tuple[bool, List[tuple[str, int, str]]]:
|
||||
"""Check if required ports are available.
|
||||
|
||||
Returns:
|
||||
Tuple of (all_available, conflicts) where conflicts is a list of
|
||||
(service_name, port, error_message) tuples
|
||||
"""
|
||||
import socket
|
||||
|
||||
service_ports = self._extract_ports_from_compose()
|
||||
conflicts: List[tuple[str, int, str]] = []
|
||||
|
||||
for service_name, ports in service_ports.items():
|
||||
for port in ports:
|
||||
try:
|
||||
# Try to bind to the port to check if it's available
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(0.5)
|
||||
result = sock.connect_ex(('127.0.0.1', port))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
# Port is in use
|
||||
conflicts.append((
|
||||
service_name,
|
||||
port,
|
||||
f"Port {port} is already in use"
|
||||
))
|
||||
except Exception as e:
|
||||
logger.debug(f"Error checking port {port}: {e}")
|
||||
continue
|
||||
|
||||
return (len(conflicts) == 0, conflicts)
|
||||
|
||||
async def _run_compose_command(
|
||||
self, args: List[str], cpu_mode: Optional[bool] = None
|
||||
) -> tuple[bool, str, str]:
|
||||
|
|
@ -696,6 +787,17 @@ class ContainerManager:
|
|||
yield False, f"ERROR: Base compose file not found at {self.compose_file.absolute()}", False
|
||||
return
|
||||
|
||||
# Check for port conflicts before starting
|
||||
yield False, "Checking port availability...", False
|
||||
ports_available, conflicts = await self.check_ports_available()
|
||||
if not ports_available:
|
||||
yield False, "ERROR: Port conflicts detected:", False
|
||||
for service_name, port, error_msg in conflicts:
|
||||
yield False, f" - {service_name}: {error_msg}", False
|
||||
yield False, "Please stop the conflicting services and try again.", False
|
||||
yield False, "Services not started due to port conflicts.", False
|
||||
return
|
||||
|
||||
yield False, "Starting OpenRAG services...", False
|
||||
|
||||
missing_images: List[str] = []
|
||||
|
|
@ -718,13 +820,37 @@ class ContainerManager:
|
|||
|
||||
yield False, "Creating and starting containers...", False
|
||||
up_success = {"value": True}
|
||||
error_messages = []
|
||||
|
||||
async for message, replace_last in self._stream_compose_command(["up", "-d"], up_success, cpu_mode):
|
||||
# Detect error patterns in the output
|
||||
import re
|
||||
lower_msg = message.lower()
|
||||
|
||||
# Check for common error patterns
|
||||
if any(pattern in lower_msg for pattern in [
|
||||
"port.*already.*allocated",
|
||||
"address already in use",
|
||||
"bind.*address already in use",
|
||||
"port is already allocated"
|
||||
]):
|
||||
error_messages.append("Port conflict detected")
|
||||
up_success["value"] = False
|
||||
elif "error" in lower_msg or "failed" in lower_msg:
|
||||
# Generic error detection
|
||||
if message not in error_messages:
|
||||
error_messages.append(message)
|
||||
|
||||
yield False, message, replace_last
|
||||
|
||||
if up_success["value"]:
|
||||
yield True, "Services started successfully", False
|
||||
else:
|
||||
yield False, "Failed to start services. See output above for details.", False
|
||||
if error_messages:
|
||||
yield False, "\nDetected errors:", False
|
||||
for err in error_messages[:5]: # Limit to first 5 errors
|
||||
yield False, f" - {err}", False
|
||||
|
||||
async def stop_services(self) -> AsyncIterator[tuple[bool, str]]:
|
||||
"""Stop all services and yield progress updates."""
|
||||
|
|
|
|||
|
|
@ -143,6 +143,29 @@ class DoclingManager:
|
|||
self._external_process = False
|
||||
return False
|
||||
|
||||
def check_port_available(self) -> tuple[bool, Optional[str]]:
|
||||
"""Check if the native service port is available.
|
||||
|
||||
Returns:
|
||||
Tuple of (available, error_message)
|
||||
"""
|
||||
import socket
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(0.5)
|
||||
result = sock.connect_ex(('127.0.0.1', self._port))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
# Port is in use
|
||||
return False, f"Port {self._port} is already in use"
|
||||
return True, None
|
||||
except Exception as e:
|
||||
logger.debug(f"Error checking port {self._port}: {e}")
|
||||
# If we can't check, assume it's available
|
||||
return True, None
|
||||
|
||||
def get_status(self) -> Dict[str, Any]:
|
||||
"""Get current status of docling serve."""
|
||||
# Check for starting state first
|
||||
|
|
|
|||
|
|
@ -319,17 +319,46 @@ class MonitorScreen(Screen):
|
|||
"""Start services with progress updates."""
|
||||
self.operation_in_progress = True
|
||||
try:
|
||||
# Check for port conflicts before attempting to start
|
||||
ports_available, conflicts = await self.container_manager.check_ports_available()
|
||||
if not ports_available:
|
||||
# Show error notification instead of modal
|
||||
conflict_msgs = []
|
||||
for service_name, port, error_msg in conflicts[:3]: # Show first 3
|
||||
conflict_msgs.append(f"{service_name} (port {port})")
|
||||
|
||||
conflict_str = ", ".join(conflict_msgs)
|
||||
if len(conflicts) > 3:
|
||||
conflict_str += f" and {len(conflicts) - 3} more"
|
||||
|
||||
self.notify(
|
||||
f"Cannot start services: Port conflicts detected for {conflict_str}. "
|
||||
f"Please stop the conflicting services first.",
|
||||
severity="error",
|
||||
timeout=10
|
||||
)
|
||||
# Refresh to show current state
|
||||
await self._refresh_services()
|
||||
return
|
||||
|
||||
# Show command output in modal dialog
|
||||
command_generator = self.container_manager.start_services(cpu_mode)
|
||||
modal = CommandOutputModal(
|
||||
"Starting Services",
|
||||
command_generator,
|
||||
on_complete=None, # We'll refresh in on_screen_resume instead
|
||||
on_complete=self._on_start_complete, # Refresh after completion
|
||||
)
|
||||
self.app.push_screen(modal)
|
||||
except Exception as e:
|
||||
self.notify(f"Error starting services: {str(e)}", severity="error")
|
||||
await self._refresh_services()
|
||||
finally:
|
||||
self.operation_in_progress = False
|
||||
|
||||
async def _on_start_complete(self) -> None:
|
||||
"""Callback after service start completes."""
|
||||
await self._refresh_services()
|
||||
|
||||
async def _stop_services(self) -> None:
|
||||
"""Stop services with progress updates."""
|
||||
self.operation_in_progress = True
|
||||
|
|
@ -394,6 +423,19 @@ class MonitorScreen(Screen):
|
|||
"""Start docling serve."""
|
||||
self.operation_in_progress = True
|
||||
try:
|
||||
# Check for port conflicts before attempting to start
|
||||
port_available, error_msg = self.docling_manager.check_port_available()
|
||||
if not port_available:
|
||||
self.notify(
|
||||
f"Cannot start docling serve: {error_msg}. "
|
||||
f"Please stop the conflicting service first.",
|
||||
severity="error",
|
||||
timeout=10
|
||||
)
|
||||
# Refresh to show current state
|
||||
await self._refresh_services()
|
||||
return
|
||||
|
||||
# Start the service (this sets _starting = True internally at the start)
|
||||
# Create task and let it begin executing (which sets the flag)
|
||||
start_task = asyncio.create_task(self.docling_manager.start())
|
||||
|
|
|
|||
|
|
@ -385,6 +385,34 @@ class WelcomeScreen(Screen):
|
|||
|
||||
async def _start_all_services(self) -> None:
|
||||
"""Start all services: containers first, then native services."""
|
||||
# Check for port conflicts before attempting to start anything
|
||||
conflicts = []
|
||||
|
||||
# Check container ports
|
||||
if self.container_manager.is_available():
|
||||
ports_available, port_conflicts = await self.container_manager.check_ports_available()
|
||||
if not ports_available:
|
||||
for service_name, port, error_msg in port_conflicts[:3]: # Show first 3
|
||||
conflicts.append(f"{service_name} (port {port})")
|
||||
if len(port_conflicts) > 3:
|
||||
conflicts.append(f"and {len(port_conflicts) - 3} more")
|
||||
|
||||
# Check native service port
|
||||
port_available, error_msg = self.docling_manager.check_port_available()
|
||||
if not port_available:
|
||||
conflicts.append(f"docling (port {self.docling_manager._port})")
|
||||
|
||||
# If there are any conflicts, show error and return
|
||||
if conflicts:
|
||||
conflict_str = ", ".join(conflicts)
|
||||
self.notify(
|
||||
f"Cannot start services: Port conflicts detected for {conflict_str}. "
|
||||
f"Please stop the conflicting services first.",
|
||||
severity="error",
|
||||
timeout=10
|
||||
)
|
||||
return
|
||||
|
||||
# Step 1: Start container services first (to create the network)
|
||||
if self.container_manager.is_available():
|
||||
command_generator = self.container_manager.start_services()
|
||||
|
|
@ -410,6 +438,20 @@ class WelcomeScreen(Screen):
|
|||
async def _start_native_services_after_containers(self) -> None:
|
||||
"""Start native services after containers have been started."""
|
||||
if not self.docling_manager.is_running():
|
||||
# Check for port conflicts before attempting to start
|
||||
port_available, error_msg = self.docling_manager.check_port_available()
|
||||
if not port_available:
|
||||
self.notify(
|
||||
f"Cannot start native services: {error_msg}. "
|
||||
f"Please stop the conflicting service first.",
|
||||
severity="error",
|
||||
timeout=10
|
||||
)
|
||||
# Update state and return
|
||||
self.docling_running = False
|
||||
await self._refresh_welcome_content()
|
||||
return
|
||||
|
||||
self.notify("Starting native services...", severity="information")
|
||||
success, message = await self.docling_manager.start()
|
||||
if success:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class CommandOutputModal(ModalScreen):
|
|||
("p", "pause_waves", "Pause"),
|
||||
("f", "speed_up", "Faster"),
|
||||
("s", "speed_down", "Slower"),
|
||||
("escape", "close_modal", "Close"),
|
||||
]
|
||||
|
||||
DEFAULT_CSS = """
|
||||
|
|
@ -188,6 +189,8 @@ class CommandOutputModal(ModalScreen):
|
|||
self._output_lines: list[str] = []
|
||||
self._layer_line_map: dict[str, int] = {} # Maps layer ID to line index
|
||||
self._status_task: Optional[asyncio.Task] = None
|
||||
self._error_detected = False
|
||||
self._command_complete = False
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
"""Create the modal dialog layout."""
|
||||
|
|
@ -254,6 +257,12 @@ class CommandOutputModal(ModalScreen):
|
|||
for w in waves.wavelets:
|
||||
w.speed = max(0.1, w.speed * 0.8)
|
||||
|
||||
def action_close_modal(self) -> None:
|
||||
"""Close the modal (only if error detected or command complete)."""
|
||||
close_btn = self.query_one("#close-btn", Button)
|
||||
if not close_btn.disabled:
|
||||
self.dismiss()
|
||||
|
||||
async def _run_command(self) -> None:
|
||||
"""Run the command and update the output in real-time."""
|
||||
output = self.query_one("#command-output", TextArea)
|
||||
|
|
@ -273,8 +282,25 @@ class CommandOutputModal(ModalScreen):
|
|||
# Move cursor to end to trigger scroll
|
||||
output.move_cursor((len(self._output_lines), 0))
|
||||
|
||||
# Detect error patterns in messages
|
||||
import re
|
||||
lower_msg = message.lower() if message else ""
|
||||
if not self._error_detected and any(pattern in lower_msg for pattern in [
|
||||
"error:",
|
||||
"failed",
|
||||
"port.*already.*allocated",
|
||||
"address already in use",
|
||||
"not found",
|
||||
"permission denied"
|
||||
]):
|
||||
self._error_detected = True
|
||||
# Enable close button when error detected
|
||||
close_btn = self.query_one("#close-btn", Button)
|
||||
close_btn.disabled = False
|
||||
|
||||
# If command is complete, update UI
|
||||
if is_complete:
|
||||
self._command_complete = True
|
||||
self._update_output("Command completed successfully", False)
|
||||
output.text = "\n".join(self._output_lines)
|
||||
output.move_cursor((len(self._output_lines), 0))
|
||||
|
|
|
|||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -2352,7 +2352,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "openrag"
|
||||
version = "0.1.37"
|
||||
version = "0.1.40"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "agentd" },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue