diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..19de73af --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,155 @@ +name: Bug Report +description: Report a bug or unexpected behavior in OpenRAG +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug! Please fill out the form below to help us understand and fix the issue. + + - type: input + id: openrag-version + attributes: + label: OpenRAG Version + description: What version of OpenRAG are you using? Run `openrag --version` or check your package version. + placeholder: "e.g., 0.1.0" + validations: + required: true + + - type: dropdown + id: deployment-method + attributes: + label: Deployment Method + description: How are you running OpenRAG? + options: + - uvx (uvx openrag) + - uv add (installed in project) + - Docker + - Podman + - Local development (make dev) + - Other + validations: + required: true + + - type: input + id: os + attributes: + label: Operating System + description: What operating system are you using? + placeholder: "e.g., macOS 14.0, Ubuntu 22.04, Windows 11" + validations: + required: true + + - type: input + id: python-version + attributes: + label: Python Version + description: What Python version are you using? Run `python --version` to check. + placeholder: "e.g., 3.13.0" + validations: + required: false + + - type: dropdown + id: affected-area + attributes: + label: Affected Area + description: Which area(s) of OpenRAG does this bug affect? Select all that apply. + multiple: true + options: + - Ingestion (document processing, upload, Docling) + - Retrieval (search, OpenSearch, hybrid search) + - Chat (chat interface, conversations, AI responses) + - Knowledge Filters (partitions, document filtering) + - Settings (configuration, model providers) + - TUI (Terminal User Interface) + - Connectors (Google Drive, OneDrive, SharePoint) + - Frontend (Next.js UI, components) + - Backend/API (Python/Starlette) + - Infrastructure (Docker, OpenSearch, Langflow) + - SDK (Python or TypeScript SDK) + - Onboarding (setup wizard, initial configuration) + - Authentication (OIDC, API keys) + - Other + validations: + required: true + + - type: textarea + id: bug-description + attributes: + label: Bug Description + description: A clear and concise description of what the bug is. + placeholder: Describe the bug... + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. Go to '...' + 2. Click on '...' + 3. Scroll down to '...' + 4. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + placeholder: What should have happened? + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: A clear and concise description of what actually happened. + placeholder: What actually happened? + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant Logs + description: | + Please copy and paste any relevant log output. + You can get logs using `make logs` for Docker deployments or check the terminal output. + This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context about the problem here (e.g., browser version, specific document types, model provider being used). + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: Checklist + description: Please confirm the following before submitting. + options: + - label: I have searched existing issues to ensure this bug hasn't been reported before. + required: true + - label: I have provided all the requested information. + required: true + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..a39c8e77 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,15 @@ +blank_issues_enabled: false +contact_links: + - name: OpenRAG Documentation + url: https://docs.openr.ag/ + about: Learn more about OpenRAG's features, installation, and configuration. + - name: Troubleshooting Guide + url: https://docs.openr.ag/support/troubleshoot + about: Check the troubleshooting guide for common issues and solutions. + - name: GitHub Discussions + url: https://github.com/langflow-ai/openrag/discussions + about: Ask questions and discuss ideas with the community. + - name: Contributing Guide + url: https://github.com/langflow-ai/openrag/blob/main/CONTRIBUTING.md + about: Learn how to contribute to OpenRAG development. + diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 00000000..9ba1eb39 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,106 @@ +name: Documentation Issue +description: Report an issue with documentation or request new documentation +title: "[Docs]: " +labels: ["documentation"] +body: + - type: markdown + attributes: + value: | + Thanks for helping improve OpenRAG's documentation! Please provide details about the issue or your request. + + - type: dropdown + id: issue-type + attributes: + label: Issue Type + description: What type of documentation issue is this? + options: + - Incorrect information + - Missing documentation + - Outdated content + - Unclear or confusing + - Typo or grammatical error + - Broken links + - Request for new documentation + - Other + validations: + required: true + + - type: dropdown + id: doc-area + attributes: + label: Documentation Area + description: Which area of documentation does this relate to? + multiple: true + options: + - Getting Started / Quickstart + - Installation (uvx, Docker, Podman) + - Configuration / Settings + - Ingestion & Document Processing + - Search & Retrieval + - Chat Interface + - Knowledge Filters + - Connectors (Google Drive, OneDrive, SharePoint) + - TUI (Terminal User Interface) + - API Reference + - SDK Documentation (Python/TypeScript) + - Troubleshooting + - Contributing Guide + - Other + validations: + required: true + + - type: input + id: doc-url + attributes: + label: Documentation URL + description: If applicable, provide a link to the specific documentation page. + placeholder: "https://docs.openr.ag/..." + validations: + required: false + + - type: textarea + id: current-content + attributes: + label: Current Content + description: If reporting an issue, what does the documentation currently say? + placeholder: Quote or describe the current documentation content. + validations: + required: false + + - type: textarea + id: issue-description + attributes: + label: Issue Description + description: Describe the problem or what documentation you'd like to see added. + placeholder: | + For issues: Explain what's wrong or confusing about the current documentation. + For requests: Describe what topic you'd like documented and why it would be helpful. + validations: + required: true + + - type: textarea + id: suggested-content + attributes: + label: Suggested Content + description: If you have suggestions for how to fix or improve the documentation, please share them. + placeholder: Provide suggested text, corrections, or an outline for new documentation. + validations: + required: false + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context, screenshots, or examples here. + validations: + required: false + + - type: checkboxes + id: contribution + attributes: + label: Contribution + description: Would you be interested in contributing to fix this documentation issue? + options: + - label: I would be willing to submit a pull request to fix this issue. + required: false + diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..f6966dca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,113 @@ +name: Feature Request +description: Suggest a new feature or enhancement for OpenRAG +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature! Please provide as much detail as possible to help us understand your request. + + - type: dropdown + id: feature-area + attributes: + label: Feature Area + description: Which area(s) of OpenRAG does this feature relate to? + multiple: true + options: + - Ingestion (document processing, upload, Docling) + - Retrieval (search, OpenSearch, hybrid search) + - Chat (chat interface, conversations, AI responses) + - Knowledge Filters (partitions, document filtering) + - Settings (configuration, model providers) + - TUI (Terminal User Interface) + - Connectors (Google Drive, OneDrive, SharePoint) + - Frontend (Next.js UI, components) + - Backend/API (Python/Starlette) + - Infrastructure (Docker, OpenSearch, Langflow) + - SDK (Python or TypeScript SDK) + - Onboarding (setup wizard, initial configuration) + - Authentication (OIDC, API keys) + - New Area + validations: + required: true + + - type: textarea + id: problem-description + attributes: + label: Problem Description + description: Is your feature request related to a problem? Please describe. + placeholder: A clear and concise description of what the problem is. E.g., "I'm always frustrated when..." + validations: + required: true + + - type: textarea + id: proposed-solution + attributes: + label: Proposed Solution + description: Describe the solution you'd like to see implemented. + placeholder: A clear and concise description of what you want to happen. + validations: + required: true + + - type: textarea + id: use-case + attributes: + label: Use Case + description: Describe your use case and how this feature would benefit you or others. + placeholder: | + As a [type of user], I want [goal] so that [benefit]. + + Example: As a developer, I want to filter documents by custom metadata so that I can organize my knowledge base more effectively. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Describe any alternative solutions or features you've considered. + placeholder: What other approaches have you thought about? Why wouldn't they work as well? + validations: + required: false + + - type: dropdown + id: priority + attributes: + label: Priority + description: How important is this feature to your workflow? + options: + - Nice to have + - Would improve my workflow + - Critical for my use case + validations: + required: true + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context, mockups, screenshots, or examples about the feature request here. + validations: + required: false + + - type: checkboxes + id: contribution + attributes: + label: Contribution + description: Would you be interested in contributing to this feature? + options: + - label: I would be willing to help implement this feature. + required: false + - label: I can help test this feature once implemented. + required: false + + - type: checkboxes + id: checklist + attributes: + label: Checklist + description: Please confirm the following before submitting. + options: + - label: I have searched existing issues and discussions to ensure this feature hasn't been requested before. + required: true + diff --git a/.github/workflows/build-multiarch.yml b/.github/workflows/build-multiarch.yml index 1f06553c..8460ecf0 100644 --- a/.github/workflows/build-multiarch.yml +++ b/.github/workflows/build-multiarch.yml @@ -206,7 +206,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.13' diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml index e1eb045c..fcbc86dd 100644 --- a/.github/workflows/dependency-audit.yml +++ b/.github/workflows/dependency-audit.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.11' diff --git a/.github/workflows/deploy-docs-draft.yml b/.github/workflows/deploy-docs-draft.yml index 23040384..7bad9982 100644 --- a/.github/workflows/deploy-docs-draft.yml +++ b/.github/workflows/deploy-docs-draft.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 20.20.0 cache: npm cache-dependency-path: ./docs/package-lock.json diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml index a751638f..171a752f 100644 --- a/.github/workflows/deploy-gh-pages.yml +++ b/.github/workflows/deploy-gh-pages.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 20.20.0 cache: npm cache-dependency-path: ./docs/package-lock.json diff --git a/.github/workflows/publish-sdk-python.yml b/.github/workflows/publish-sdk-python.yml index 928c9d11..bb1c878a 100644 --- a/.github/workflows/publish-sdk-python.yml +++ b/.github/workflows/publish-sdk-python.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.12' diff --git a/.github/workflows/update-uv-lock.yml b/.github/workflows/update-uv-lock.yml index 2ebc8ea5..b3eadb1f 100644 --- a/.github/workflows/update-uv-lock.yml +++ b/.github/workflows/update-uv-lock.yml @@ -20,7 +20,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.13' diff --git a/Dockerfile.frontend b/Dockerfile.frontend index f46b431d..3ab6d8ac 100644 --- a/Dockerfile.frontend +++ b/Dockerfile.frontend @@ -1,4 +1,4 @@ -FROM node:18-slim +FROM node:20.20.0-slim # Set working directory WORKDIR /app diff --git a/docs/docs/_partial-gpu-mode-tip.mdx b/docs/docs/_partial-gpu-mode-tip.mdx new file mode 100644 index 00000000..d9d229fb --- /dev/null +++ b/docs/docs/_partial-gpu-mode-tip.mdx @@ -0,0 +1,5 @@ +GPU acceleration isn't required for most use cases. +OpenRAG's CPU-only deployment doesn't prevent you from using GPU acceleration in external services, such as Ollama servers. + +GPU acceleration is required only for specific use cases, typically involving customization of the ingestion flows or ingestion logic. +For example, writing alternate ingest logic in OpenRAG that uses GPUs directly in the container, or customizing the ingestion flows to use Langflow's Docling component with GPU acceleration instead of OpenRAG's `docling serve` service. \ No newline at end of file diff --git a/docs/docs/_partial-prereq-common.mdx b/docs/docs/_partial-prereq-common.mdx index 4682d8cc..621273c7 100644 --- a/docs/docs/_partial-prereq-common.mdx +++ b/docs/docs/_partial-prereq-common.mdx @@ -15,4 +15,6 @@ If a provider offers only one type, you must select two providers. ::: -* Optional: Install GPU support with an NVIDIA GPU, [CUDA](https://docs.nvidia.com/cuda/) support, and compatible NVIDIA drivers on the OpenRAG host machine. If you don't have GPU capabilities, OpenRAG provides an alternate CPU-only deployment. \ No newline at end of file +* Optional: Install GPU support with an NVIDIA GPU, [CUDA](https://docs.nvidia.com/cuda/) support, and compatible NVIDIA drivers on the OpenRAG host machine. +If you don't have GPU capabilities, OpenRAG provides an alternate CPU-only deployment that is suitable for most use cases. +The default CPU-only deployment doesn't prevent you from using GPU acceleration in external services, such as Ollama servers. \ No newline at end of file diff --git a/docs/docs/get-started/docker.mdx b/docs/docs/get-started/docker.mdx index 811d3388..1fc4e798 100644 --- a/docs/docs/get-started/docker.mdx +++ b/docs/docs/get-started/docker.mdx @@ -12,6 +12,7 @@ import PartialPrereqWindows from '@site/docs/_partial-prereq-windows.mdx'; import PartialPrereqPython from '@site/docs/_partial-prereq-python.mdx'; import PartialInstallNextSteps from '@site/docs/_partial-install-next-steps.mdx'; import PartialOllamaModels from '@site/docs/_partial-ollama-models.mdx'; +import PartialGpuModeTip from '@site/docs/_partial-gpu-mode-tip.mdx'; To manage your own OpenRAG services, deploy OpenRAG with Docker or Podman. @@ -116,7 +117,17 @@ The following variables are required or recommended: 3. Deploy the OpenRAG containers locally using the appropriate Docker Compose configuration for your environment: - * **GPU-accelerated deployment**: If your host machine has an NVIDIA GPU with CUDA support and compatible NVIDIA drivers, use the base `docker-compose.yml` file with the `docker-compose.gpu.yml` override. + * **CPU-only deployment** (default, recommended): If your host machine doesn't have NVIDIA GPU support, use the base `docker-compose.yml` file: + + ```bash title="Docker" + docker compose up -d + ``` + + ```bash title="Podman" + podman compose up -d + ``` + + * **GPU-accelerated deployment**: If your host machine has an NVIDIA GPU with CUDA support and compatible NVIDIA drivers, use the base `docker-compose.yml` file with the `docker-compose.gpu.yml` override: ```bash title="Docker" docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d @@ -126,15 +137,9 @@ The following variables are required or recommended: podman compose -f docker-compose.yml -f docker-compose.gpu.yml up -d ``` - * **CPU-only deployment** (default): If your host machine doesn't have NVIDIA GPU support, use the base `docker-compose.yml` file. - - ```bash title="Docker" - docker compose up -d - ``` - - ```bash title="Podman" - podman compose up -d - ``` + :::tip + + ::: 4. Wait for the OpenRAG containers to start, and then confirm that all containers are running: diff --git a/docs/docs/get-started/tui.mdx b/docs/docs/get-started/tui.mdx index 2a6c36dc..7a192c67 100644 --- a/docs/docs/get-started/tui.mdx +++ b/docs/docs/get-started/tui.mdx @@ -3,6 +3,8 @@ title: Use the TUI slug: /tui --- +import PartialGpuModeTip from '@site/docs/_partial-gpu-mode-tip.mdx'; + The OpenRAG Terminal User Interface (TUI) provides a simplified and guided experience for configuring, managing, and monitoring your OpenRAG deployment directly from the terminal. ![OpenRAG TUI Interface](@site/static/img/openrag_tui_dec_2025.png) @@ -36,6 +38,10 @@ In the TUI, click **Status**, and then click **Switch to GPU Mode** or **Switch This change requires restarting all OpenRAG services because each mode has its own `docker-compose` file. +:::tip + +::: + ## Exit the OpenRAG TUI To exit the OpenRAG TUI, press q on the TUI main page. diff --git a/docs/package-lock.json b/docs/package-lock.json index 41a104d0..a41d11e3 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -26,7 +26,7 @@ "typescript": "~5.9.3" }, "engines": { - "node": ">=18.0" + "node": ">=20.20.0" } }, "node_modules/@ai-sdk/gateway": { diff --git a/docs/package.json b/docs/package.json index 2bd2f7f4..041bb207 100644 --- a/docs/package.json +++ b/docs/package.json @@ -46,6 +46,6 @@ ] }, "engines": { - "node": ">=18.0" + "node": ">=20.20.0" } } diff --git a/frontend/package.json b/frontend/package.json index 897f0c4c..ca131f61 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,6 +2,9 @@ "name": "frontend", "version": "0.1.0", "private": true, + "engines": { + "node": ">=20.20.0" + }, "scripts": { "dev": "next dev", "build": "next build", diff --git a/helm/openrag/Chart.yaml b/helm/openrag/Chart.yaml new file mode 100644 index 00000000..ac9d0df3 --- /dev/null +++ b/helm/openrag/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: openrag +description: A Helm chart for deploying OpenRAG - an open-source agentic RAG platform +type: application +version: 0.1.0 +appVersion: "0.1.52" +keywords: + - openrag + - rag + - langflow + - opensearch + - ai + - llm +home: https://github.com/langflow-ai/openrag +sources: + - https://github.com/langflow-ai/openrag +maintainers: + - name: OpenRAG Team diff --git a/helm/openrag/templates/NOTES.txt b/helm/openrag/templates/NOTES.txt new file mode 100644 index 00000000..932754ba --- /dev/null +++ b/helm/openrag/templates/NOTES.txt @@ -0,0 +1,80 @@ +OpenRAG has been deployed successfully! + +{{- if .Values.global.tenant.name }} +Tenant: {{ .Values.global.tenant.name }} +Namespace: {{ include "openrag.namespace" . }} +{{- end }} + +=== Services Deployed === + +{{- if .Values.langflow.enabled }} +Langflow: + - Internal URL: http://{{ include "openrag.fullname" . }}-langflow:{{ .Values.langflow.service.port }} + {{- if and .Values.ingress.enabled .Values.ingress.hosts.langflow.enabled .Values.ingress.hosts.langflow.host }} + - External URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.hosts.langflow.host }} + {{- end }} +{{- end }} + +{{- if .Values.backend.enabled }} +Backend API: + - Internal URL: http://{{ include "openrag.fullname" . }}-backend:{{ .Values.backend.service.port }} + {{- if and .Values.ingress.enabled .Values.ingress.hosts.backend.host }} + - External URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.hosts.backend.host }} + {{- end }} +{{- end }} + +{{- if .Values.frontend.enabled }} +Frontend UI: + - Internal URL: http://{{ include "openrag.fullname" . }}-frontend:{{ .Values.frontend.service.port }} + {{- if and .Values.ingress.enabled .Values.ingress.hosts.frontend.host }} + - External URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.hosts.frontend.host }} + {{- end }} +{{- end }} + +{{- if .Values.dashboards.enabled }} +OpenSearch Dashboards: + - Internal URL: http://{{ include "openrag.fullname" . }}-dashboards:{{ .Values.dashboards.service.port }} + {{- if and .Values.ingress.enabled .Values.ingress.hosts.dashboards.enabled .Values.ingress.hosts.dashboards.host }} + - External URL: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.hosts.dashboards.host }} + {{- end }} +{{- end }} + +=== External Dependencies === + +OpenSearch (External SaaS): + - Host: {{ .Values.global.opensearch.host }} + - Port: {{ .Values.global.opensearch.port }} + - Scheme: {{ .Values.global.opensearch.scheme }} + +=== Credentials === + +To retrieve the Langflow superuser credentials: + + kubectl get secret {{ include "openrag.fullname" . }}-langflow -n {{ include "openrag.namespace" . }} -o jsonpath='{.data.superuser}' | base64 -d + kubectl get secret {{ include "openrag.fullname" . }}-langflow -n {{ include "openrag.namespace" . }} -o jsonpath='{.data.superuser-password}' | base64 -d + +=== Quick Start === + +{{- if and .Values.ingress.enabled .Values.ingress.hosts.frontend.host }} +1. Access the OpenRAG UI at: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.hosts.frontend.host }} +{{- else }} +1. Port-forward the frontend service: + kubectl port-forward svc/{{ include "openrag.fullname" . }}-frontend {{ .Values.frontend.service.port }}:{{ .Values.frontend.service.port }} -n {{ include "openrag.namespace" . }} + Then access: http://localhost:{{ .Values.frontend.service.port }} +{{- end }} + +2. Upload documents to your knowledge base + +3. Start chatting with your documents using the AI-powered chat interface + +=== Troubleshooting === + +Check pod status: + kubectl get pods -n {{ include "openrag.namespace" . }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Check pod logs: + kubectl logs -n {{ include "openrag.namespace" . }} -l app.kubernetes.io/component=langflow + kubectl logs -n {{ include "openrag.namespace" . }} -l app.kubernetes.io/component=backend + kubectl logs -n {{ include "openrag.namespace" . }} -l app.kubernetes.io/component=frontend + +For more information, visit: https://github.com/langflow-ai/openrag diff --git a/helm/openrag/templates/_helpers.tpl b/helm/openrag/templates/_helpers.tpl new file mode 100644 index 00000000..c0a49b85 --- /dev/null +++ b/helm/openrag/templates/_helpers.tpl @@ -0,0 +1,163 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "openrag.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +If tenant name is provided, prefix with tenant name. +*/}} +{{- define "openrag.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if .Values.global.tenant.name }} +{{- printf "%s-%s" .Values.global.tenant.name $name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create the namespace name. +Uses tenant namespace if specified, otherwise tenant name, otherwise release namespace. +*/}} +{{- define "openrag.namespace" -}} +{{- if .Values.global.tenant.namespace }} +{{- .Values.global.tenant.namespace }} +{{- else if .Values.global.tenant.name }} +{{- .Values.global.tenant.name }} +{{- else }} +{{- .Release.Namespace }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "openrag.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "openrag.labels" -}} +helm.sh/chart: {{ include "openrag.chart" . }} +{{ include "openrag.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.global.tenant.name }} +openrag.io/tenant: {{ .Values.global.tenant.name }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "openrag.selectorLabels" -}} +app.kubernetes.io/name: {{ include "openrag.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "openrag.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "openrag.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Langflow component labels +*/}} +{{- define "openrag.langflow.labels" -}} +{{ include "openrag.labels" . }} +app.kubernetes.io/component: langflow +{{- end }} + +{{/* +Langflow selector labels +*/}} +{{- define "openrag.langflow.selectorLabels" -}} +{{ include "openrag.selectorLabels" . }} +app.kubernetes.io/component: langflow +{{- end }} + +{{/* +Backend component labels +*/}} +{{- define "openrag.backend.labels" -}} +{{ include "openrag.labels" . }} +app.kubernetes.io/component: backend +{{- end }} + +{{/* +Backend selector labels +*/}} +{{- define "openrag.backend.selectorLabels" -}} +{{ include "openrag.selectorLabels" . }} +app.kubernetes.io/component: backend +{{- end }} + +{{/* +Frontend component labels +*/}} +{{- define "openrag.frontend.labels" -}} +{{ include "openrag.labels" . }} +app.kubernetes.io/component: frontend +{{- end }} + +{{/* +Frontend selector labels +*/}} +{{- define "openrag.frontend.selectorLabels" -}} +{{ include "openrag.selectorLabels" . }} +app.kubernetes.io/component: frontend +{{- end }} + +{{/* +Dashboards component labels +*/}} +{{- define "openrag.dashboards.labels" -}} +{{ include "openrag.labels" . }} +app.kubernetes.io/component: dashboards +{{- end }} + +{{/* +Dashboards selector labels +*/}} +{{- define "openrag.dashboards.selectorLabels" -}} +{{ include "openrag.selectorLabels" . }} +app.kubernetes.io/component: dashboards +{{- end }} + +{{/* +Generate the Langflow service URL +*/}} +{{- define "openrag.langflow.url" -}} +http://{{ include "openrag.fullname" . }}-langflow:{{ .Values.langflow.service.port }} +{{- end }} + +{{/* +Generate the Backend service URL +*/}} +{{- define "openrag.backend.url" -}} +http://{{ include "openrag.fullname" . }}-backend:{{ .Values.backend.service.port }} +{{- end }} + +{{/* +Generate the OpenSearch URL +*/}} +{{- define "openrag.opensearch.url" -}} +{{ .Values.global.opensearch.scheme }}://{{ .Values.global.opensearch.host }}:{{ .Values.global.opensearch.port }} +{{- end }} diff --git a/helm/openrag/templates/backend/deployment.yaml b/helm/openrag/templates/backend/deployment.yaml new file mode 100644 index 00000000..aa6077ba --- /dev/null +++ b/helm/openrag/templates/backend/deployment.yaml @@ -0,0 +1,273 @@ +{{- if .Values.backend.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "openrag.fullname" . }}-backend + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.backend.labels" . | nindent 4 }} +spec: + replicas: 1 # Single pod for vertical scaling + strategy: + type: Recreate # Required for RWO PVCs + selector: + matchLabels: + {{- include "openrag.backend.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "openrag.backend.selectorLabels" . | nindent 8 }} + annotations: + checksum/secret-opensearch: {{ include (print $.Template.BasePath "/secrets/opensearch-secret.yaml") . | sha256sum }} + checksum/secret-langflow: {{ include (print $.Template.BasePath "/secrets/langflow-secret.yaml") . | sha256sum }} + checksum/config-flows: {{ include (print $.Template.BasePath "/configmaps/flow-ids-configmap.yaml") . | sha256sum }} + checksum/config-app: {{ include (print $.Template.BasePath "/configmaps/app-config-configmap.yaml") . | sha256sum }} + spec: + serviceAccountName: {{ include "openrag.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.langflow.enabled }} + initContainers: + - name: wait-for-langflow + image: busybox:1.36 + command: + - sh + - -c + - | + echo "Waiting for Langflow to be ready..." + until nc -z {{ include "openrag.fullname" . }}-langflow {{ .Values.langflow.service.port }}; do + echo "Langflow not ready, sleeping 5s..." + sleep 5 + done + echo "Langflow is ready!" + {{- end }} + containers: + - name: backend + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Values.global.imageTag }}" + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + # OpenSearch connection (external SaaS) + - name: OPENSEARCH_HOST + value: {{ .Values.global.opensearch.host | quote }} + - name: OPENSEARCH_PORT + value: {{ .Values.global.opensearch.port | quote }} + - name: OPENSEARCH_USERNAME + value: {{ .Values.global.opensearch.username | quote }} + {{- if .Values.global.opensearch.password }} + - name: OPENSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-opensearch + key: password + {{- end }} + # Langflow connection + - name: LANGFLOW_URL + value: {{ include "openrag.langflow.url" . | quote }} + {{- if .Values.backend.langflowPublicUrl }} + - name: LANGFLOW_PUBLIC_URL + value: {{ .Values.backend.langflowPublicUrl | quote }} + {{- end }} + - name: LANGFLOW_AUTO_LOGIN + value: {{ .Values.langflow.auth.autoLogin | quote }} + - name: LANGFLOW_SUPERUSER + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-langflow + key: superuser + - name: LANGFLOW_SUPERUSER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-langflow + key: superuser-password + # Flow IDs from ConfigMap + - name: LANGFLOW_CHAT_FLOW_ID + valueFrom: + configMapKeyRef: + name: {{ include "openrag.fullname" . }}-flow-ids + key: chat-flow-id + - name: LANGFLOW_INGEST_FLOW_ID + valueFrom: + configMapKeyRef: + name: {{ include "openrag.fullname" . }}-flow-ids + key: ingest-flow-id + - name: LANGFLOW_URL_INGEST_FLOW_ID + valueFrom: + configMapKeyRef: + name: {{ include "openrag.fullname" . }}-flow-ids + key: url-ingest-flow-id + - name: NUDGES_FLOW_ID + valueFrom: + configMapKeyRef: + name: {{ include "openrag.fullname" . }}-flow-ids + key: nudges-flow-id + # Feature flags + - name: DISABLE_INGEST_WITH_LANGFLOW + value: {{ .Values.backend.features.disableIngestWithLangflow | quote }} + # LLM Provider keys + {{- if .Values.llmProviders.openai.enabled }} + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: openai-api-key + {{- end }} + {{- if .Values.llmProviders.anthropic.enabled }} + - name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: anthropic-api-key + {{- end }} + {{- if .Values.llmProviders.watsonx.enabled }} + - name: WATSONX_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-api-key + - name: WATSONX_ENDPOINT + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-endpoint + - name: WATSONX_PROJECT_ID + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-project-id + {{- end }} + {{- if .Values.llmProviders.ollama.enabled }} + - name: OLLAMA_ENDPOINT + value: {{ .Values.llmProviders.ollama.endpoint | quote }} + {{- end }} + # OAuth credentials + {{- if .Values.global.oauth.google.enabled }} + - name: GOOGLE_OAUTH_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-oauth + key: google-client-id + - name: GOOGLE_OAUTH_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-oauth + key: google-client-secret + {{- end }} + {{- if .Values.global.oauth.microsoft.enabled }} + - name: MICROSOFT_GRAPH_OAUTH_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-oauth + key: microsoft-client-id + - name: MICROSOFT_GRAPH_OAUTH_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-oauth + key: microsoft-client-secret + {{- end }} + # AWS credentials + {{- if .Values.backend.aws.enabled }} + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-aws + key: access-key-id + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-aws + key: secret-access-key + {{- end }} + # Webhook configuration + {{- if .Values.backend.webhook.enabled }} + - name: WEBHOOK_BASE_URL + value: {{ .Values.backend.webhook.baseUrl | quote }} + {{- end }} + volumeMounts: + {{- if .Values.backend.persistence.documents.enabled }} + - name: documents + mountPath: {{ .Values.backend.persistence.documents.mountPath }} + {{- end }} + {{- if .Values.backend.persistence.keys.enabled }} + - name: keys + mountPath: {{ .Values.backend.persistence.keys.mountPath }} + {{- end }} + {{- if .Values.backend.persistence.config.enabled }} + - name: config + mountPath: {{ .Values.backend.persistence.config.mountPath }} + {{- end }} + {{- if .Values.langflow.persistence.enabled }} + - name: flows + mountPath: /app/flows + subPath: {{ .Values.langflow.persistence.flowsSubPath }} + readOnly: true + {{- end }} + resources: + {{- toYaml .Values.backend.resources | nindent 12 }} + {{- if .Values.backend.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.backend.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.backend.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.backend.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.backend.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.backend.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.backend.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.backend.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.backend.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.backend.readinessProbe.failureThreshold }} + {{- end }} + volumes: + {{- if .Values.backend.persistence.documents.enabled }} + - name: documents + persistentVolumeClaim: + claimName: {{ include "openrag.fullname" . }}-documents + {{- end }} + {{- if .Values.backend.persistence.keys.enabled }} + - name: keys + persistentVolumeClaim: + claimName: {{ include "openrag.fullname" . }}-keys + {{- end }} + {{- if .Values.backend.persistence.config.enabled }} + - name: config + persistentVolumeClaim: + claimName: {{ include "openrag.fullname" . }}-config + {{- end }} + {{- if .Values.langflow.persistence.enabled }} + - name: flows + persistentVolumeClaim: + claimName: {{ include "openrag.fullname" . }}-langflow + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/backend/service.yaml b/helm/openrag/templates/backend/service.yaml new file mode 100644 index 00000000..d105de86 --- /dev/null +++ b/helm/openrag/templates/backend/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.backend.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "openrag.fullname" . }}-backend + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.backend.labels" . | nindent 4 }} +spec: + type: {{ .Values.backend.service.type }} + ports: + - port: {{ .Values.backend.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "openrag.backend.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/openrag/templates/configmaps/app-config-configmap.yaml b/helm/openrag/templates/configmaps/app-config-configmap.yaml new file mode 100644 index 00000000..e04dea4c --- /dev/null +++ b/helm/openrag/templates/configmaps/app-config-configmap.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-app-config + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + config.yaml: | + agent: + llm_model: {{ .Values.appConfig.agent.llmModel | quote }} + llm_provider: {{ .Values.appConfig.agent.llmProvider | quote }} + {{- if .Values.appConfig.agent.systemPrompt }} + system_prompt: {{ .Values.appConfig.agent.systemPrompt | quote }} + {{- end }} + edited: false + knowledge: + chunk_overlap: {{ .Values.appConfig.knowledge.chunkOverlap }} + chunk_size: {{ .Values.appConfig.knowledge.chunkSize }} + embedding_model: {{ .Values.appConfig.knowledge.embeddingModel | quote }} + embedding_provider: {{ .Values.appConfig.knowledge.embeddingProvider | quote }} + ocr: {{ .Values.appConfig.knowledge.ocr }} + picture_descriptions: {{ .Values.appConfig.knowledge.pictureDescriptions }} + table_structure: {{ .Values.appConfig.knowledge.tableStructure }} + providers: + anthropic: + configured: {{ .Values.llmProviders.anthropic.enabled }} + ollama: + configured: {{ .Values.llmProviders.ollama.enabled }} + {{- if .Values.llmProviders.ollama.endpoint }} + endpoint: {{ .Values.llmProviders.ollama.endpoint | quote }} + {{- end }} + openai: + configured: {{ .Values.llmProviders.openai.enabled }} + watsonx: + configured: {{ .Values.llmProviders.watsonx.enabled }} + {{- if .Values.llmProviders.watsonx.endpoint }} + endpoint: {{ .Values.llmProviders.watsonx.endpoint | quote }} + {{- end }} diff --git a/helm/openrag/templates/configmaps/flow-ids-configmap.yaml b/helm/openrag/templates/configmaps/flow-ids-configmap.yaml new file mode 100644 index 00000000..4e4dfb9c --- /dev/null +++ b/helm/openrag/templates/configmaps/flow-ids-configmap.yaml @@ -0,0 +1,14 @@ +{{- if .Values.langflow.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-flow-ids + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + chat-flow-id: {{ .Values.langflow.flows.chatFlowId | quote }} + ingest-flow-id: {{ .Values.langflow.flows.ingestFlowId | quote }} + url-ingest-flow-id: {{ .Values.langflow.flows.urlIngestFlowId | quote }} + nudges-flow-id: {{ .Values.langflow.flows.nudgesFlowId | quote }} +{{- end }} diff --git a/helm/openrag/templates/configmaps/flows/agent-flow-configmap.yaml b/helm/openrag/templates/configmaps/flows/agent-flow-configmap.yaml new file mode 100644 index 00000000..cff35fe9 --- /dev/null +++ b/helm/openrag/templates/configmaps/flows/agent-flow-configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.langflow.flows.loadDefaults }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-flow-agent + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + openrag_agent.json: |- +{{ .Files.Get "flows/openrag_agent.json" | indent 4 }} +{{- end }} diff --git a/helm/openrag/templates/configmaps/flows/ingestion-flow-configmap.yaml b/helm/openrag/templates/configmaps/flows/ingestion-flow-configmap.yaml new file mode 100644 index 00000000..ad236517 --- /dev/null +++ b/helm/openrag/templates/configmaps/flows/ingestion-flow-configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.langflow.flows.loadDefaults }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-flow-ingestion + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + ingestion_flow.json: |- +{{ .Files.Get "flows/ingestion_flow.json" | indent 4 }} +{{- end }} diff --git a/helm/openrag/templates/configmaps/flows/nudges-flow-configmap.yaml b/helm/openrag/templates/configmaps/flows/nudges-flow-configmap.yaml new file mode 100644 index 00000000..5ebe05fb --- /dev/null +++ b/helm/openrag/templates/configmaps/flows/nudges-flow-configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.langflow.flows.loadDefaults }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-flow-nudges + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + openrag_nudges.json: |- +{{ .Files.Get "flows/openrag_nudges.json" | indent 4 }} +{{- end }} diff --git a/helm/openrag/templates/configmaps/flows/url-flow-configmap.yaml b/helm/openrag/templates/configmaps/flows/url-flow-configmap.yaml new file mode 100644 index 00000000..911ee5e4 --- /dev/null +++ b/helm/openrag/templates/configmaps/flows/url-flow-configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.langflow.flows.loadDefaults }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "openrag.fullname" . }}-flow-url + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +data: + openrag_url_mcp.json: |- +{{ .Files.Get "flows/openrag_url_mcp.json" | indent 4 }} +{{- end }} diff --git a/helm/openrag/templates/dashboards/deployment.yaml b/helm/openrag/templates/dashboards/deployment.yaml new file mode 100644 index 00000000..49d87d3a --- /dev/null +++ b/helm/openrag/templates/dashboards/deployment.yaml @@ -0,0 +1,81 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "openrag.fullname" . }}-dashboards + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.dashboards.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.dashboards.replicaCount }} + selector: + matchLabels: + {{- include "openrag.dashboards.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "openrag.dashboards.selectorLabels" . | nindent 8 }} + annotations: + checksum/secret: {{ include (print $.Template.BasePath "/secrets/opensearch-secret.yaml") . | sha256sum }} + spec: + serviceAccountName: {{ include "openrag.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: dashboards + image: "{{ .Values.dashboards.image.repository }}:{{ .Values.dashboards.image.tag }}" + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + ports: + - name: http + containerPort: 5601 + protocol: TCP + env: + # OpenSearch connection (external SaaS) + - name: OPENSEARCH_HOSTS + value: '["{{ include "openrag.opensearch.url" . }}"]' + - name: OPENSEARCH_USERNAME + value: {{ .Values.global.opensearch.username | quote }} + {{- if .Values.global.opensearch.password }} + - name: OPENSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-opensearch + key: password + {{- end }} + resources: + {{- toYaml .Values.dashboards.resources | nindent 12 }} + {{- if .Values.dashboards.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /api/status + port: http + initialDelaySeconds: {{ .Values.dashboards.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.dashboards.livenessProbe.periodSeconds }} + {{- end }} + {{- if .Values.dashboards.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /api/status + port: http + initialDelaySeconds: {{ .Values.dashboards.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.dashboards.readinessProbe.periodSeconds }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/dashboards/service.yaml b/helm/openrag/templates/dashboards/service.yaml new file mode 100644 index 00000000..91830518 --- /dev/null +++ b/helm/openrag/templates/dashboards/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "openrag.fullname" . }}-dashboards + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.dashboards.labels" . | nindent 4 }} +spec: + type: {{ .Values.dashboards.service.type }} + ports: + - port: {{ .Values.dashboards.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "openrag.dashboards.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/openrag/templates/frontend/deployment.yaml b/helm/openrag/templates/frontend/deployment.yaml new file mode 100644 index 00000000..4c7ec937 --- /dev/null +++ b/helm/openrag/templates/frontend/deployment.yaml @@ -0,0 +1,80 @@ +{{- if .Values.frontend.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "openrag.fullname" . }}-frontend + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.frontend.labels" . | nindent 4 }} +spec: + {{- if not .Values.frontend.autoscaling.enabled }} + replicas: {{ .Values.frontend.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "openrag.frontend.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "openrag.frontend.selectorLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "openrag.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: frontend + image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Values.global.imageTag }}" + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 3000 + protocol: TCP + env: + # Backend connection (uses internal service name) + - name: OPENRAG_BACKEND_HOST + value: "{{ include "openrag.fullname" . }}-backend" + resources: + {{- toYaml .Values.frontend.resources | nindent 12 }} + {{- if .Values.frontend.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: {{ .Values.frontend.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.frontend.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.frontend.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.frontend.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.frontend.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: {{ .Values.frontend.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.frontend.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.frontend.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.frontend.readinessProbe.failureThreshold }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/frontend/hpa.yaml b/helm/openrag/templates/frontend/hpa.yaml new file mode 100644 index 00000000..306b0108 --- /dev/null +++ b/helm/openrag/templates/frontend/hpa.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.frontend.enabled .Values.frontend.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "openrag.fullname" . }}-frontend + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.frontend.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "openrag.fullname" . }}-frontend + minReplicas: {{ .Values.frontend.autoscaling.minReplicas }} + maxReplicas: {{ .Values.frontend.autoscaling.maxReplicas }} + metrics: + {{- if .Values.frontend.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.frontend.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.frontend.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.frontend.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/frontend/service.yaml b/helm/openrag/templates/frontend/service.yaml new file mode 100644 index 00000000..1a3b7c8c --- /dev/null +++ b/helm/openrag/templates/frontend/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.frontend.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "openrag.fullname" . }}-frontend + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.frontend.labels" . | nindent 4 }} +spec: + type: {{ .Values.frontend.service.type }} + ports: + - port: {{ .Values.frontend.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "openrag.frontend.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/openrag/templates/ingress/ingress.yaml b/helm/openrag/templates/ingress/ingress.yaml new file mode 100644 index 00000000..5e03e5bd --- /dev/null +++ b/helm/openrag/templates/ingress/ingress.yaml @@ -0,0 +1,104 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "openrag.fullname" . }} + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.ingress.tls.certManager.enabled }} + cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.certManager.issuerRef.name }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls.enabled }} + tls: + {{- if .Values.ingress.hosts.frontend.host }} + - hosts: + - {{ .Values.ingress.hosts.frontend.host }} + secretName: {{ .Values.ingress.tls.secretName | default (printf "%s-frontend-tls" (include "openrag.fullname" .)) }} + {{- end }} + {{- if .Values.ingress.hosts.backend.host }} + - hosts: + - {{ .Values.ingress.hosts.backend.host }} + secretName: {{ .Values.ingress.tls.secretName | default (printf "%s-backend-tls" (include "openrag.fullname" .)) }} + {{- end }} + {{- if and .Values.ingress.hosts.langflow.enabled .Values.ingress.hosts.langflow.host }} + - hosts: + - {{ .Values.ingress.hosts.langflow.host }} + secretName: {{ .Values.ingress.tls.secretName | default (printf "%s-langflow-tls" (include "openrag.fullname" .)) }} + {{- end }} + {{- if and .Values.dashboards.enabled .Values.ingress.hosts.dashboards.enabled .Values.ingress.hosts.dashboards.host }} + - hosts: + - {{ .Values.ingress.hosts.dashboards.host }} + secretName: {{ .Values.ingress.tls.secretName | default (printf "%s-dashboards-tls" (include "openrag.fullname" .)) }} + {{- end }} + {{- end }} + rules: + {{- if .Values.ingress.hosts.frontend.host }} + # Frontend ingress rule + - host: {{ .Values.ingress.hosts.frontend.host }} + http: + paths: + {{- range .Values.ingress.hosts.frontend.paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "openrag.fullname" $ }}-frontend + port: + number: {{ $.Values.frontend.service.port }} + {{- end }} + {{- end }} + {{- if .Values.ingress.hosts.backend.host }} + # Backend API ingress rule + - host: {{ .Values.ingress.hosts.backend.host }} + http: + paths: + {{- range .Values.ingress.hosts.backend.paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "openrag.fullname" $ }}-backend + port: + number: {{ $.Values.backend.service.port }} + {{- end }} + {{- end }} + {{- if and .Values.ingress.hosts.langflow.enabled .Values.ingress.hosts.langflow.host }} + # Optional Langflow direct access + - host: {{ .Values.ingress.hosts.langflow.host }} + http: + paths: + {{- range .Values.ingress.hosts.langflow.paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "openrag.fullname" $ }}-langflow + port: + number: {{ $.Values.langflow.service.port }} + {{- end }} + {{- end }} + {{- if and .Values.dashboards.enabled .Values.ingress.hosts.dashboards.enabled .Values.ingress.hosts.dashboards.host }} + # Optional Dashboards access + - host: {{ .Values.ingress.hosts.dashboards.host }} + http: + paths: + {{- range .Values.ingress.hosts.dashboards.paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "openrag.fullname" $ }}-dashboards + port: + number: {{ $.Values.dashboards.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/langflow/deployment.yaml b/helm/openrag/templates/langflow/deployment.yaml new file mode 100644 index 00000000..54ba3db8 --- /dev/null +++ b/helm/openrag/templates/langflow/deployment.yaml @@ -0,0 +1,247 @@ +{{- if .Values.langflow.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "openrag.fullname" . }}-langflow + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.langflow.labels" . | nindent 4 }} +spec: + replicas: 1 # Always 1 for SQLite-based Langflow + strategy: + type: Recreate # Required for RWO PVC + selector: + matchLabels: + {{- include "openrag.langflow.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "openrag.langflow.selectorLabels" . | nindent 8 }} + annotations: + checksum/secret: {{ include (print $.Template.BasePath "/secrets/langflow-secret.yaml") . | sha256sum }} + checksum/config: {{ include (print $.Template.BasePath "/configmaps/flow-ids-configmap.yaml") . | sha256sum }} + spec: + serviceAccountName: {{ include "openrag.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.langflow.flows.loadDefaults }} + initContainers: + - name: load-default-flows + image: busybox:1.36 + command: + - /bin/sh + - -c + - | + FLOWS_DIR="{{ .Values.langflow.persistence.mountPath }}/{{ .Values.langflow.persistence.flowsSubPath }}" + mkdir -p "$FLOWS_DIR" + if [ -z "$(ls -A $FLOWS_DIR 2>/dev/null)" ]; then + echo "Loading default flows..." + cp /default-flows/*.json "$FLOWS_DIR/" + echo "Flows loaded: $(ls $FLOWS_DIR)" + else + echo "Flows already exist, skipping." + fi + volumeMounts: + - name: langflow-data + mountPath: {{ .Values.langflow.persistence.mountPath }} + - name: default-flows + mountPath: /default-flows + {{- end }} + containers: + - name: langflow + image: "{{ .Values.langflow.image.repository }}:{{ .Values.langflow.image.tag | default .Values.global.imageTag }}" + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 7860 + protocol: TCP + env: + # Langflow core settings + - name: LANGFLOW_LOAD_FLOWS_PATH + value: {{ .Values.langflow.persistence.mountPath }}/{{ .Values.langflow.persistence.flowsSubPath }} + - name: LANGFLOW_DATABASE_URL + value: "sqlite:///{{ .Values.langflow.persistence.mountPath }}/{{ .Values.langflow.persistence.dbSubPath }}" + - name: LANGFLOW_DEACTIVATE_TRACING + value: {{ .Values.langflow.deactivateTracing | quote }} + - name: LANGFLOW_LOG_LEVEL + value: {{ .Values.langflow.logLevel | quote }} + - name: HIDE_GETTING_STARTED_PROGRESS + value: "true" + # Auth settings + - name: LANGFLOW_AUTO_LOGIN + value: {{ .Values.langflow.auth.autoLogin | quote }} + - name: LANGFLOW_NEW_USER_IS_ACTIVE + value: {{ .Values.langflow.auth.newUserIsActive | quote }} + - name: LANGFLOW_ENABLE_SUPERUSER_CLI + value: {{ .Values.langflow.auth.enableSuperuserCli | quote }} + # Variables to expose to flows + - name: LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT + value: {{ .Values.langflow.variablesToGetFromEnvironment | quote }} + # Flow context variables (defaults for flow execution) + - name: JWT + value: "None" + - name: OWNER + value: "None" + - name: OWNER_NAME + value: "None" + - name: OWNER_EMAIL + value: "None" + - name: CONNECTOR_TYPE + value: "system" + - name: CONNECTOR_TYPE_URL + value: "url" + - name: OPENRAG-QUERY-FILTER + value: "{}" + - name: FILENAME + value: "None" + - name: MIMETYPE + value: "None" + - name: FILESIZE + value: "0" + - name: SELECTED_EMBEDDING_MODEL + value: "" + # Secrets from langflow secret + - name: LANGFLOW_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-langflow + key: secret-key + - name: LANGFLOW_SUPERUSER + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-langflow + key: superuser + - name: LANGFLOW_SUPERUSER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-langflow + key: superuser-password + # OpenSearch password (for flows) + {{- if .Values.global.opensearch.password }} + - name: OPENSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-opensearch + key: password + {{- end }} + # LLM Provider keys + {{- if .Values.llmProviders.openai.enabled }} + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: openai-api-key + {{- else }} + - name: OPENAI_API_KEY + value: "None" + {{- end }} + {{- if .Values.llmProviders.anthropic.enabled }} + - name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: anthropic-api-key + {{- else }} + - name: ANTHROPIC_API_KEY + value: "None" + {{- end }} + {{- if .Values.llmProviders.watsonx.enabled }} + - name: WATSONX_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-api-key + - name: WATSONX_ENDPOINT + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-endpoint + - name: WATSONX_PROJECT_ID + valueFrom: + secretKeyRef: + name: {{ include "openrag.fullname" . }}-llm-providers + key: watsonx-project-id + {{- else }} + - name: WATSONX_API_KEY + value: "None" + - name: WATSONX_ENDPOINT + value: "None" + - name: WATSONX_PROJECT_ID + value: "None" + {{- end }} + {{- if .Values.llmProviders.ollama.enabled }} + - name: OLLAMA_BASE_URL + value: {{ .Values.llmProviders.ollama.endpoint | quote }} + {{- else }} + - name: OLLAMA_BASE_URL + value: "None" + {{- end }} + volumeMounts: + - name: langflow-data + mountPath: {{ .Values.langflow.persistence.mountPath }} + resources: + {{- toYaml .Values.langflow.resources | nindent 12 }} + {{- if .Values.langflow.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.langflow.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.langflow.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.langflow.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.langflow.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.langflow.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.langflow.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.langflow.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.langflow.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.langflow.readinessProbe.failureThreshold }} + {{- end }} + volumes: + - name: langflow-data + {{- if .Values.langflow.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "openrag.fullname" . }}-langflow + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.langflow.flows.loadDefaults }} + - name: default-flows + projected: + sources: + - configMap: + name: {{ include "openrag.fullname" . }}-flow-ingestion + - configMap: + name: {{ include "openrag.fullname" . }}-flow-agent + - configMap: + name: {{ include "openrag.fullname" . }}-flow-nudges + - configMap: + name: {{ include "openrag.fullname" . }}-flow-url + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/langflow/service.yaml b/helm/openrag/templates/langflow/service.yaml new file mode 100644 index 00000000..ae9c98e1 --- /dev/null +++ b/helm/openrag/templates/langflow/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.langflow.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "openrag.fullname" . }}-langflow + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.langflow.labels" . | nindent 4 }} +spec: + type: {{ .Values.langflow.service.type }} + ports: + - port: {{ .Values.langflow.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "openrag.langflow.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/openrag/templates/secrets/aws-secret.yaml b/helm/openrag/templates/secrets/aws-secret.yaml new file mode 100644 index 00000000..b20ad07a --- /dev/null +++ b/helm/openrag/templates/secrets/aws-secret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.backend.aws.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openrag.fullname" . }}-aws + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +type: Opaque +stringData: + access-key-id: {{ .Values.backend.aws.accessKeyId | quote }} + secret-access-key: {{ .Values.backend.aws.secretAccessKey | quote }} +{{- end }} diff --git a/helm/openrag/templates/secrets/langflow-secret.yaml b/helm/openrag/templates/secrets/langflow-secret.yaml new file mode 100644 index 00000000..e43e07b1 --- /dev/null +++ b/helm/openrag/templates/secrets/langflow-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.langflow.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openrag.fullname" . }}-langflow + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +type: Opaque +stringData: + secret-key: {{ .Values.langflow.auth.secretKey | default (randAlphaNum 32) | quote }} + superuser: {{ .Values.langflow.auth.superuser | quote }} + superuser-password: {{ .Values.langflow.auth.superuserPassword | default (randAlphaNum 16) | quote }} +{{- end }} diff --git a/helm/openrag/templates/secrets/llm-providers-secret.yaml b/helm/openrag/templates/secrets/llm-providers-secret.yaml new file mode 100644 index 00000000..9c394c2a --- /dev/null +++ b/helm/openrag/templates/secrets/llm-providers-secret.yaml @@ -0,0 +1,22 @@ +{{- if or .Values.llmProviders.openai.enabled .Values.llmProviders.anthropic.enabled .Values.llmProviders.watsonx.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openrag.fullname" . }}-llm-providers + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +type: Opaque +stringData: + {{- if .Values.llmProviders.openai.enabled }} + openai-api-key: {{ .Values.llmProviders.openai.apiKey | quote }} + {{- end }} + {{- if .Values.llmProviders.anthropic.enabled }} + anthropic-api-key: {{ .Values.llmProviders.anthropic.apiKey | quote }} + {{- end }} + {{- if .Values.llmProviders.watsonx.enabled }} + watsonx-api-key: {{ .Values.llmProviders.watsonx.apiKey | quote }} + watsonx-endpoint: {{ .Values.llmProviders.watsonx.endpoint | quote }} + watsonx-project-id: {{ .Values.llmProviders.watsonx.projectId | quote }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/secrets/oauth-secret.yaml b/helm/openrag/templates/secrets/oauth-secret.yaml new file mode 100644 index 00000000..4742c42b --- /dev/null +++ b/helm/openrag/templates/secrets/oauth-secret.yaml @@ -0,0 +1,19 @@ +{{- if or .Values.global.oauth.google.enabled .Values.global.oauth.microsoft.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openrag.fullname" . }}-oauth + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +type: Opaque +stringData: + {{- if .Values.global.oauth.google.enabled }} + google-client-id: {{ .Values.global.oauth.google.clientId | quote }} + google-client-secret: {{ .Values.global.oauth.google.clientSecret | quote }} + {{- end }} + {{- if .Values.global.oauth.microsoft.enabled }} + microsoft-client-id: {{ .Values.global.oauth.microsoft.clientId | quote }} + microsoft-client-secret: {{ .Values.global.oauth.microsoft.clientSecret | quote }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/secrets/opensearch-secret.yaml b/helm/openrag/templates/secrets/opensearch-secret.yaml new file mode 100644 index 00000000..07909041 --- /dev/null +++ b/helm/openrag/templates/secrets/opensearch-secret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.global.opensearch.password }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openrag.fullname" . }}-opensearch + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} +type: Opaque +stringData: + password: {{ .Values.global.opensearch.password | quote }} + username: {{ .Values.global.opensearch.username | quote }} +{{- end }} diff --git a/helm/openrag/templates/serviceaccount.yaml b/helm/openrag/templates/serviceaccount.yaml new file mode 100644 index 00000000..aa4f377b --- /dev/null +++ b/helm/openrag/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "openrag.serviceAccountName" . }} + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/helm/openrag/templates/storage/config-pvc.yaml b/helm/openrag/templates/storage/config-pvc.yaml new file mode 100644 index 00000000..d5ced07f --- /dev/null +++ b/helm/openrag/templates/storage/config-pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.backend.enabled .Values.backend.persistence.config.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "openrag.fullname" . }}-config + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.backend.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.backend.persistence.config.accessMode }} + {{- if .Values.backend.persistence.config.storageClass }} + storageClassName: {{ .Values.backend.persistence.config.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.backend.persistence.config.size }} +{{- end }} diff --git a/helm/openrag/templates/storage/documents-pvc.yaml b/helm/openrag/templates/storage/documents-pvc.yaml new file mode 100644 index 00000000..03ee3401 --- /dev/null +++ b/helm/openrag/templates/storage/documents-pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.backend.enabled .Values.backend.persistence.documents.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "openrag.fullname" . }}-documents + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.backend.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.backend.persistence.documents.accessMode }} + {{- if .Values.backend.persistence.documents.storageClass }} + storageClassName: {{ .Values.backend.persistence.documents.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.backend.persistence.documents.size }} +{{- end }} diff --git a/helm/openrag/templates/storage/keys-pvc.yaml b/helm/openrag/templates/storage/keys-pvc.yaml new file mode 100644 index 00000000..6d2acd4c --- /dev/null +++ b/helm/openrag/templates/storage/keys-pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.backend.enabled .Values.backend.persistence.keys.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "openrag.fullname" . }}-keys + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.backend.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.backend.persistence.keys.accessMode }} + {{- if .Values.backend.persistence.keys.storageClass }} + storageClassName: {{ .Values.backend.persistence.keys.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.backend.persistence.keys.size }} +{{- end }} diff --git a/helm/openrag/templates/storage/langflow-pvc.yaml b/helm/openrag/templates/storage/langflow-pvc.yaml new file mode 100644 index 00000000..b1a7e21c --- /dev/null +++ b/helm/openrag/templates/storage/langflow-pvc.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.langflow.enabled .Values.langflow.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "openrag.fullname" . }}-langflow + namespace: {{ include "openrag.namespace" . }} + labels: + {{- include "openrag.langflow.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.langflow.persistence.accessMode }} + {{- if .Values.langflow.persistence.storageClass }} + storageClassName: {{ .Values.langflow.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.langflow.persistence.size }} +{{- end }} diff --git a/helm/openrag/values.yaml b/helm/openrag/values.yaml new file mode 100644 index 00000000..ade7e1fa --- /dev/null +++ b/helm/openrag/values.yaml @@ -0,0 +1,408 @@ +# OpenRAG Helm Chart Values +# This chart deploys OpenRAG with external OpenSearch SaaS connection + +# Override names +nameOverride: "" +fullnameOverride: "" + +# Global settings +global: + # Tenant identification - used for resource naming and namespace + tenant: + name: "" # Required for multi-tenant: tenant identifier (e.g., "acme") + namespace: "" # Optional: override namespace (defaults to tenant name or release namespace) + + # Image settings + imageRegistry: "langflowai" + imagePullPolicy: IfNotPresent + imageTag: "latest" # Override with specific version in production + imagePullSecrets: [] + + # External OpenSearch SaaS connection (OpenSearch is NOT deployed by this chart) + opensearch: + host: "" # Required: OpenSearch SaaS endpoint (e.g., "my-cluster.us-east-1.es.amazonaws.com") + port: 443 # Default HTTPS port for managed OpenSearch + scheme: "https" # https for production SaaS + username: "admin" # OpenSearch username + password: "" # OpenSearch password (stored in secret) + + # Shared OAuth credentials (same across all tenants) + oauth: + google: + enabled: false + clientId: "" # Google OAuth client ID + clientSecret: "" # Google OAuth client secret + microsoft: + enabled: false + clientId: "" # Microsoft Graph OAuth client ID + clientSecret: "" # Microsoft Graph OAuth client secret + +# ============================================================================ +# Langflow Configuration +# ============================================================================ +langflow: + enabled: true + + image: + repository: langflowai/openrag-langflow + tag: "" # Uses global.imageTag if empty + + # Single pod - vertical scaling only (SQLite requires single writer) + replicaCount: 1 + + # Resource requests/limits for vertical scaling + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "4" + memory: "8Gi" + + # Persistence for SQLite DB and flows + persistence: + enabled: true + storageClass: "" # Empty uses cluster default + accessMode: ReadWriteOnce + size: 10Gi + mountPath: /app/data + flowsSubPath: flows + dbSubPath: langflow.db + + # Flow configuration (UUIDs for Langflow workflows) + flows: + loadDefaults: true # Load default OpenRAG flows on first deployment + chatFlowId: "1098eea1-6649-4e1d-aed1-b77249fb8dd0" + ingestFlowId: "5488df7c-b93f-4f87-a446-b67028bc0813" + urlIngestFlowId: "72c3d17c-2dac-4a73-b48a-6518473d7830" + nudgesFlowId: "ebc01d31-1976-46ce-a385-b0240327226c" + loadPath: /app/flows + + # Authentication settings + auth: + autoLogin: false + superuser: "admin" # Langflow superuser username + superuserPassword: "" # Langflow superuser password (stored in secret) + secretKey: "" # Langflow secret key for JWT (stored in secret) + newUserIsActive: false + enableSuperuserCli: false + + # Runtime settings + deactivateTracing: true + logLevel: "INFO" # DEBUG, INFO, WARNING, ERROR + + # Variables to expose to flows + variablesToGetFromEnvironment: "JWT,OPENRAG-QUERY-FILTER,OPENSEARCH_PASSWORD,OWNER,OWNER_NAME,OWNER_EMAIL,CONNECTOR_TYPE,FILENAME,MIMETYPE,FILESIZE,SELECTED_EMBEDDING_MODEL,OPENAI_API_KEY,ANTHROPIC_API_KEY,WATSONX_API_KEY,WATSONX_ENDPOINT,WATSONX_PROJECT_ID,OLLAMA_BASE_URL" + + # Probes + livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + # Service configuration + service: + type: ClusterIP + port: 7860 + +# ============================================================================ +# OpenRAG Backend Configuration +# ============================================================================ +backend: + enabled: true + + image: + repository: langflowai/openrag-backend + tag: "" # Uses global.imageTag if empty + + # Single pod for vertical scaling + replicaCount: 1 + + # Resource requests/limits + resources: + requests: + cpu: "500m" + memory: "2Gi" + limits: + cpu: "4" + memory: "16Gi" + + # Persistence for documents, keys, and config + persistence: + documents: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 50Gi + mountPath: /app/openrag-documents + keys: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 1Gi + mountPath: /app/keys + config: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 1Gi + mountPath: /app/config + + # Feature flags + features: + disableIngestWithLangflow: false # Set true to use traditional processor instead of Langflow + + # Langflow public URL (for UI links to Langflow) + langflowPublicUrl: "" # e.g., "https://langflow.example.com" + + # Webhook configuration for continuous ingestion + webhook: + enabled: false + baseUrl: "" # DNS routable URL for webhooks (e.g., ngrok URL) + + # AWS credentials for S3 integration + aws: + enabled: false + accessKeyId: "" + secretAccessKey: "" + + # Probes + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + # Service configuration + service: + type: ClusterIP + port: 8000 + +# ============================================================================ +# OpenRAG Frontend Configuration +# ============================================================================ +frontend: + enabled: true + + image: + repository: langflowai/openrag-frontend + tag: "" # Uses global.imageTag if empty + + # Can be multiple replicas (stateless) + replicaCount: 2 + + # Resource requests/limits + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "1" + memory: "1Gi" + + # Horizontal Pod Autoscaler + autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + + # Probes + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + + # Service configuration + service: + type: ClusterIP + port: 3000 + +# ============================================================================ +# OpenSearch Dashboards Configuration (Optional) +# ============================================================================ +dashboards: + enabled: false # Enable only if dashboards available in OS SaaS + + image: + repository: opensearchproject/opensearch-dashboards + tag: "3.0.0" + + replicaCount: 1 + + # Resource requests/limits + resources: + requests: + cpu: "100m" + memory: "512Mi" + limits: + cpu: "1" + memory: "2Gi" + + # Probes + livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 30 + readinessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + + # Service configuration + service: + type: ClusterIP + port: 5601 + +# ============================================================================ +# Ingress Configuration +# ============================================================================ +ingress: + enabled: true + className: "nginx" # nginx, alb, traefik, etc. + + # Annotations for ingress controller + annotations: {} + # For nginx: + # nginx.ingress.kubernetes.io/proxy-body-size: "100m" + # nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + # For AWS ALB: + # alb.ingress.kubernetes.io/scheme: internet-facing + # alb.ingress.kubernetes.io/target-type: ip + + # Host configuration + hosts: + frontend: + host: "" # e.g., "openrag.example.com" + paths: + - path: / + pathType: Prefix + backend: + host: "" # e.g., "api.openrag.example.com" + paths: + - path: / + pathType: Prefix + langflow: + enabled: false # Optional: expose Langflow directly + host: "" # e.g., "langflow.openrag.example.com" + paths: + - path: / + pathType: Prefix + dashboards: + enabled: false # Only if dashboards.enabled is true + host: "" + paths: + - path: / + pathType: Prefix + + # TLS configuration + tls: + enabled: false + # Use existing secret: + # secretName: "openrag-tls" + + # Or use cert-manager: + certManager: + enabled: false + issuerRef: + name: "letsencrypt-prod" + kind: "ClusterIssuer" + +# ============================================================================ +# LLM Provider API Keys +# ============================================================================ +llmProviders: + openai: + enabled: false + apiKey: "" # OpenAI API key (stored in secret) + anthropic: + enabled: false + apiKey: "" # Anthropic API key (stored in secret) + watsonx: + enabled: false + apiKey: "" # WatsonX API key (stored in secret) + endpoint: "https://us-south.ml.cloud.ibm.com" + projectId: "" # WatsonX project ID + ollama: + enabled: false + endpoint: "" # Ollama endpoint URL (e.g., "http://ollama:11434") + +# ============================================================================ +# Application Config (config.yaml contents) +# ============================================================================ +appConfig: + agent: + llmModel: "claude-sonnet-4-5-20250929" + llmProvider: "anthropic" + # System prompt can be customized here + systemPrompt: "" # Leave empty to use default + knowledge: + chunkOverlap: 200 + chunkSize: 1000 + embeddingModel: "text-embedding-3-large" + embeddingProvider: "openai" + ocr: false + pictureDescriptions: false + tableStructure: true + +# ============================================================================ +# Service Account +# ============================================================================ +serviceAccount: + create: true + name: "" + annotations: {} + +# ============================================================================ +# Pod Security +# ============================================================================ +podSecurityContext: + fsGroup: 1000 + runAsNonRoot: true + +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsUser: 1000 + runAsGroup: 1000 + +# ============================================================================ +# Node Placement +# ============================================================================ +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# ============================================================================ +# Pod Disruption Budgets +# ============================================================================ +podDisruptionBudget: + enabled: false + minAvailable: 1 + # maxUnavailable: 1 diff --git a/src/main.py b/src/main.py index 710b3dab..fd2bf2a7 100644 --- a/src/main.py +++ b/src/main.py @@ -21,6 +21,7 @@ from functools import partial from starlette.applications import Starlette from starlette.routing import Route +from starlette.responses import JSONResponse # Set multiprocessing start method to 'spawn' for CUDA compatibility multiprocessing.set_start_method("spawn", force=True) @@ -456,6 +457,24 @@ async def _ingest_default_documents_langflow(services, file_paths): file_count=len(file_paths), ) +async def opensearch_health_ready(request): + """Readiness probe: verifies OpenSearch dependency is reachable.""" + try: + # Fast check that the cluster is reachable/auth works + await asyncio.wait_for(clients.opensearch.info(), timeout=5.0) + return JSONResponse( + {"status": "ready", "dependencies": {"opensearch": "up"}}, + status_code=200, + ) + except Exception as e: + return JSONResponse( + { + "status": "not_ready", + "dependencies": {"opensearch": "down"}, + "error": str(e), + }, + status_code=503, + ) async def _ingest_default_documents_openrag(services, file_paths): """Ingest default documents using traditional OpenRAG processor.""" @@ -1148,6 +1167,11 @@ async def create_app(): ), methods=["GET"], ), + Route( + "/search/health", + opensearch_health_ready, + methods=["GET"], + ), # Models endpoints Route( "/models/openai",