Skip to content

Commit 3c0fd9e

Browse files
LCORE-3048 : okp_rag e2e test (#2553)
* LCORE-3048: add Konflux E2E coverage for OKP Solr RAG Co-authored-by: Cursor <cursoragent@cursor.com> * updated ls config to ogx confif * renamd remnants of llama stack reference to ogx * added a note on how to run OKP tests * updated providers to the latest commit, removed skip tag from one scenario * Revert providers submodule pin; image uses the Konflux zip. Co-authored-by: Cursor <cursoragent@cursor.com> * removed negative yaml, updated llama reference to ogx * removing tag from makerfile * added extra logs, cleaned up okp rag py * addressing present-but-null response fields * updating health check url * updated cmd_check_okp_solr_from_llama to use the same portal-rag select URL as the probes * updating PVC * adding search mode --------- Co-authored-by: Sofiya Nuryyeva <sonuryye@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4fe1074 commit 3c0fd9e

25 files changed

Lines changed: 1362 additions & 101 deletions

‎.konflux/artifacts.lock.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ metadata:
33
version: "1.0"
44

55
artifacts:
6-
- download_url: https://github.com/lightspeed-core/lightspeed-providers/archive/8cd1b3d3bdd841ea99d31b334ae00a275581661c.zip
7-
checksum: sha256:6e660a30babcd1c00eef1686b251f9368410e03e08a7c0d4a680db00693ac448
6+
- download_url: https://github.com/lightspeed-core/lightspeed-providers/archive/c095177e7d6d58a4009556251bbbca96ebf3dcb4.zip
7+
checksum: sha256:2146f5b1d4850c437db024498ef4ec2a49ea760a413d25269ca4641f11f4b093
88
filename: lightspeed-providers.zip

‎.tekton/integration-tests/pipeline/lightspeed-stack-integration-test.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ spec:
212212
- name: quay-aipcc-password
213213
secret:
214214
secretName: quay-aipcc-password
215+
# Red Hat registry pull secret (docker-registry type) - will be copied to test namespace
216+
- name: redhat-registry-pull-secret-source
217+
secret:
218+
secretName: redhat-registry-io-pull-secret
215219
- name: credentials
216220
emptyDir: {}
217221
steps:
@@ -251,6 +255,9 @@ spec:
251255
mountPath: /var/run/quay-aipcc-name
252256
- name: quay-aipcc-password
253257
mountPath: /var/run/quay-aipcc-password
258+
# Red Hat registry pull secret (docker-registry type)
259+
- name: redhat-registry-pull-secret-source
260+
mountPath: /var/run/redhat-registry-pull-secret
254261
- name: credentials
255262
mountPath: /credentials
256263
env:

‎Makefile‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
SHELL := /bin/bash
22
#TODO: We need to rename all those python and config files as well
33

4+
# Define comma for use in $(if) expressions (where commas are argument separators)
5+
COMMA := ,
6+
47
ARTIFACT_DIR := $(if $(ARTIFACT_DIR),$(ARTIFACT_DIR),tests/test_results)
58
PATH_TO_PLANTUML := ~/bin
69

@@ -16,6 +19,7 @@ OGX_CONFIG ?= run.yaml
1619
OGX_CONTAINER_NAME ?= lightspeed-ogx
1720
OGX_IMAGE ?= lightspeed-ogx:local
1821
OGX_PORT ?= 8321
22+
LIGHTSPEED_PROVIDERS_DIR ?= $(shell [ -d ../lightspeed-providers ] && cd ../lightspeed-providers && pwd)
1923
CONTAINER_RUNTIME ?= $(shell command -v podman 2>/dev/null || command -v docker 2>/dev/null)
2024

2125
# Doc tools configuration
@@ -89,6 +93,9 @@ start-ogx-container: build-ogx-image ## Start OGX container
8993
-v $(PWD)/$(CONFIG):/opt/app-root/lightspeed-stack.yaml:ro,z \
9094
-v $(PWD)/scripts/ogx-entrypoint.sh:/opt/app-root/enrich-entrypoint.sh:ro,z \
9195
-v $(PWD)/src/ogx_configuration.py:/opt/app-root/ogx_configuration.py:ro,z \
96+
$(if $(LIGHTSPEED_PROVIDERS_DIR),-v $(LIGHTSPEED_PROVIDERS_DIR)/lightspeed_stack_providers:/opt/app-root/providers/lightspeed_stack_providers:ro$(COMMA)z) \
97+
$(if $(LIGHTSPEED_PROVIDERS_DIR),-v $(LIGHTSPEED_PROVIDERS_DIR)/resources/external_providers:/opt/app-root/src/.llama/providers.d:ro$(COMMA)z) \
98+
$(if $(LIGHTSPEED_PROVIDERS_DIR),-e EXTERNAL_PROVIDERS_DIR=/opt/app-root/src/.llama/providers.d) \
9299
-e OPENAI_API_KEY \
93100
-e BRAVE_SEARCH_API_KEY \
94101
-e TAVILY_SEARCH_API_KEY \

‎deploy/lightspeed-stack/Containerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ COPY ${LSC_SOURCE_DIR}/pyproject.toml ${LSC_SOURCE_DIR}/LICENSE ${LSC_SOURCE_DIR
3737

3838
# lightspeed-providers:
3939
# Fully hermetic — uses prefetched artifact or pinned commit from GitHub
40-
ARG LIGHTSPEED_PROVIDERS_COMMIT=faf6a89a3ad7856e2e7a934324f31d146108acdb
40+
ARG LIGHTSPEED_PROVIDERS_COMMIT=c095177e7d6d58a4009556251bbbca96ebf3dcb4
4141
RUN set -eux; \
4242
ZIP_PATH="/tmp/lightspeed-providers.zip"; \
4343
EXTRACT_DIR="/tmp/providers"; \

‎docs/testing/e2e_scenarios.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@
115115
* Check if models can be filtered
116116
* Check if filtering can return empty list of models
117117

118+
## [`okp_rag.feature`](https://github.com/lightspeed-core/lightspeed-stack/blob/main/tests/e2e/features/okp_rag.feature)
119+
120+
Konflux only (`@konflux-only`) in CI. Targeted local Behave (match YAML to OGX/LCS): [Running OKP RAG tests locally](e2e_testing.md#running-okp-rag-tests-locally).
121+
122+
* Offline mode query with inline RAG returns rag_chunks and referenced_documents
123+
* Online mode streaming query with inline RAG returns referenced_documents
124+
* Query with inline RAG with dynamic semantic filter returns rag_chunks and referenced_documents
125+
* Offline query API with OKP tool RAG has rag_chunk and referenced_documents returned
126+
* Online responses API with OKP tool RAG has rag results returned
127+
* Query succeeds with empty rag_chunks when OKP server is unavailable
128+
* Streaming query succeeds with empty referenced_documents when OKP server is unavailable
129+
* Query returns no rag_chunks and no referenced_documents when OKP is disabled
130+
* Streaming query returns no referenced_documents when OKP is disabled
131+
118132
## [`proxy.feature`](https://github.com/lightspeed-core/lightspeed-stack/blob/main/tests/e2e/features/proxy.feature)
119133

120134
* LLM traffic is routed through a configured tunnel proxy

‎docs/testing/e2e_testing.md‎

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ This guide describes how to run, extend, and understand the Lightspeed Core Stac
99
1. [Overview](#overview)
1010
2. [Directory Layout](#directory-layout)
1111
3. [How to Run E2E Tests](#how-to-run-e2e-tests)
12-
4. [Environment Variables](#environment-variables)
13-
5. [Deployment Modes: Server vs Library](#deployment-modes-server-vs-library)
14-
6. [Tags and Hooks](#tags-and-hooks)
15-
7. [Configuration Files](#configuration-files)
16-
8. [Feature Files and Steps](#feature-files-and-steps)
17-
9. [Gherkin Keywords in Feature Files](#gherkin-keywords-in-feature-files)
18-
10. [Choosing the Test Layer: E2E or Integration?](#choosing-the-test-layer-e2e-or-integration)
19-
11. [Writing New Scenarios](#writing-new-scenarios)
20-
12. [Troubleshooting](#troubleshooting)
12+
4. [Running OKP RAG tests locally](#running-okp-rag-tests-locally)
13+
5. [Environment Variables](#environment-variables)
14+
6. [Deployment Modes: Server vs Library](#deployment-modes-server-vs-library)
15+
7. [Tags and Hooks](#tags-and-hooks)
16+
8. [Configuration Files](#configuration-files)
17+
9. [Feature Files and Steps](#feature-files-and-steps)
18+
10. [Gherkin Keywords in Feature Files](#gherkin-keywords-in-feature-files)
19+
11. [Choosing the Test Layer: E2E or Integration?](#choosing-the-test-layer-e2e-or-integration)
20+
12. [Writing New Scenarios](#writing-new-scenarios)
21+
13. [Troubleshooting](#troubleshooting)
2122

2223
---
2324

@@ -136,6 +137,73 @@ uv run behave tests/e2e/features/health.feature --tags=-skip-in-library-mode
136137

137138
---
138139

140+
## Running OKP RAG tests locally
141+
142+
`okp_rag.feature` is `@konflux-only`. CI deploys OKP as a pod; `make test-e2e` skips the feature. Do not set `E2E_KONFLUX_E2E=1` locally (that path is Kubernetes). On a laptop, start OKP in Docker, enrich and run OGX and LCS as host processes, then run **one scenario that matches the YAML you started**.
143+
144+
Needs `registry.redhat.io` login, `OPENAI_API_KEY`, `uv sync --locked --group ogxlibdev`, and `../lightspeed-providers`.
145+
146+
### Prerequisite: OKP in Docker
147+
148+
```bash
149+
docker login registry.redhat.io
150+
docker run --rm -d -p 8081:8080 registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest
151+
```
152+
153+
Wait until Solr answers (not only the portal page):
154+
155+
```bash
156+
curl -sS -m 15 -o /dev/null -w "%{http_code}\n" \
157+
'http://localhost:8081/solr/portal-rag/select?q=*:*&rows=0'
158+
```
159+
160+
### 1. Create the enriched run file
161+
162+
Use the same Lightspeed YAML the scenario will load (`-c`). Example: offline inline RAG.
163+
164+
```bash
165+
export PYTHONPATH="$(cd ../lightspeed-providers && pwd)${PYTHONPATH:+:$PYTHONPATH}"
166+
export EXTERNAL_PROVIDERS_DIR="$(cd ../lightspeed-providers && pwd)/resources/external_providers"
167+
export RH_SERVER_OKP=http://localhost:8081/solr
168+
169+
uv run python src/ogx_configuration.py \
170+
-c tests/e2e/configuration/server-mode/lightspeed-stack-okp-offline.yaml \
171+
-i run.yaml \
172+
-o run_enriched.yaml
173+
```
174+
175+
### 2. Start OGX
176+
177+
```bash
178+
export PYTHONPATH="$(cd ../lightspeed-providers && pwd)${PYTHONPATH:+:$PYTHONPATH}"
179+
export EXTERNAL_PROVIDERS_DIR="$(cd ../lightspeed-providers && pwd)/resources/external_providers"
180+
181+
uv run ogx stack run run_enriched.yaml --port 8321
182+
```
183+
184+
### 3. Start LCS
185+
186+
Same `CONFIG` as `-c` in step 1. `make run-ogx` starts LCS only (`CONFIG=`, not `make -c`).
187+
188+
```bash
189+
export RH_SERVER_OKP=http://localhost:8081/solr
190+
export E2E_LLAMA_HOSTNAME=localhost
191+
LIGHTSPEED_STACK_LOG_LEVEL=DEBUG make run-ogx \
192+
CONFIG=tests/e2e/configuration/server-mode/lightspeed-stack-okp-offline.yaml
193+
```
194+
195+
### 4. Run the matching Behave scenario
196+
197+
`okp_rag.feature:24` is the offline inline scenario (`lightspeed-stack-okp-offline.yaml`). Comment `@konflux-only` on the Feature, `OKP(Solr) server is running` in Background, and `OGX is restarted` / `The service is restarted` on that scenario (OGX and LCS are already up). Do not commit those comments.
198+
199+
```bash
200+
uv run behave tests/e2e/features/okp_rag.feature:24
201+
```
202+
203+
For another YAML (`lightspeed-stack-okp-online.yaml`, tool RAG, and so on), repeat steps 1–3 with that file, then run the scenario line that uses it. Product setup: [OKP guide](../user_doc/okp_guide.md).
204+
205+
---
206+
139207
## Environment Variables
140208

141209
| Variable | Default | Description |
@@ -151,6 +219,8 @@ uv run behave tests/e2e/features/health.feature --tags=-skip-in-library-mode
151219
| `E2E_DEFAULT_PROVIDER_OVERRIDE` | — | Override default provider id (e.g. `openai`). |
152220
| `FAISS_VECTOR_STORE_ID` | — | Vector store id for FAISS-related scenarios. |
153221
| `RUNNING_PROW` | — | Set in Prow/OpenShift; enables Prow config paths and pod/container ops. |
222+
| `E2E_KONFLUX_E2E` | — | `1` in Konflux only. Unskips `@konflux-only` and deploys OKP as a pod. |
223+
| `RH_SERVER_OKP` | — | OKP/Solr URL (local default `http://localhost:8081/solr`). |
154224
| `OPENAI_API_KEY` | — | **Required.** Used by the app and OGX for LLM calls (e.g. OpenAI). The E2E tests and the stack will not run correctly without it. |
155225

156226

@@ -176,6 +246,8 @@ All tag behaviour is implemented in **`features/environment.py`**: the hooks (`b
176246
| Tag | Effect |
177247
|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
178248
| `@skip` | Scenario is skipped (reason: "Marked with @skip"). Use for broken or WIP scenarios. |
249+
| `@konflux-only` | Skipped unless `E2E_KONFLUX_E2E=1`. Used by `okp_rag.feature`. |
250+
| `@cfg_okp` | OKP Solr RAG. Konflux deploys OKP in `before_feature`. Local: [Running OKP RAG tests locally](#running-okp-rag-tests-locally). |
179251
| `@skip-in-library-mode` | Scenario is skipped when `E2E_DEPLOYMENT_MODE=library`. Used for tests that require a separate OGX (e.g. connection disruption). |
180252
| `@local` | Skipped unless running in "local" mode (context flag). |
181253
| `@InvalidFeedbackStorageConfig` | Before scenario: switch to invalid-feedback-storage config and restart container. After: restore feature config and restart. |
@@ -246,6 +318,7 @@ The feature files below are run in the order given in `tests/e2e/test_list.txt`:
246318
| `rest_api.feature` | REST API: OpenAPI endpoint. |
247319
| `mcp.feature` | MCP (Model Context Protocol): tools, query, streaming_query with MCP auth (required, token, invalid token). |
248320
| `models.feature` | Models endpoint: list models, filter, empty result; error when OGX unreachable. |
321+
| `okp_rag.feature` | OKP Solr RAG (`@konflux-only`). Local: [Running OKP RAG tests locally](#running-okp-rag-tests-locally). |
249322

250323

251324
If you add a new feature file, add it to **`tests/e2e/test_list.txt`** so it is included when you run the full E2E suite (e.g. `make test-e2e`). The order in that file is the run order.
@@ -396,6 +469,6 @@ The integration side of this boundary is described in
396469
- **Readonly database (SQLite) in OGX**: If the RAG KV DB is on a bind-mounted path that becomes read-only (e.g. after restart), move it to a named volume (e.g. via `KV_RAG_PATH` in docker-compose) so writes succeed.
397470
- **ChunkedEncodingError on streaming_query**: The step for streaming_query uses `stream=True` and consumes the stream; if you add new streaming steps, avoid reading the full response with `response.content` and use the same stream-reading pattern so a server close after an error event does not raise.
398471
- **Event loop is closed (httpx/AsyncClient)**: In E2E, any code that creates an `AsyncOgxClient` (e.g. for shields) must close it (e.g. `await client.close()`) in a `finally` block before the event loop is torn down (e.g. before `asyncio.run()` returns).
399-
- **Scenarios skipped**: Check tags (`@skip`, `@skip-in-library-mode`, `@local`) and `E2E_DEPLOYMENT_MODE`; ensure the scenario is not excluded by `--tags=-skip` (or the opposite if you intend to run only skipped scenarios for debugging).
472+
- **Scenarios skipped**: Check tags (`@skip`, `@skip-in-library-mode`, `@local`, `@konflux-only`) and `E2E_DEPLOYMENT_MODE`; ensure the scenario is not excluded by `--tags=-skip` (or the opposite if you intend to run only skipped scenarios for debugging).
400473

401474
For more on test structure and commands, see the main project guide (`CLAUDE.md`) and `tests/e2e/features/steps/README.md`.

‎docs/user_doc/okp_guide.md‎

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,22 @@ validating that queries return referenced chunks.
5151

5252
## Step 1: Launch OKP
5353

54-
Start the OKP RAG service with Podman:
54+
Start the OKP RAG service with Podman or Docker:
55+
56+
**Using Podman:**
5557

5658
```bash
59+
podman login registry.redhat.io
5760
podman run --rm -d -p 8081:8080 registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest
5861
```
5962

63+
**Using Docker:**
64+
65+
```bash
66+
docker login registry.redhat.io
67+
docker run --rm -d -p 8081:8080 registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest
68+
```
69+
6070
> **Note:** Remove `-d` to run in the foreground.
6171
6272
* The service listens on **port 8081** on the host (mapped from 8080 in the container). Lightspeed Stack itself listens on `8080`, so this avoids port conflicts.
@@ -105,41 +115,61 @@ Edit your Lightspeed Stack config file (e.g. `lightspeed-stack.yaml`) and add
105115
the following top-level sections so that OKP is used for either inline or tool
106116
RAG:
107117

108-
Inline RAG:
118+
**Inline RAG:**
109119

110120
```yaml
111121
# RAG configuration
112122
rag:
113-
inline:
114-
- okp
115-
okp:
116-
rhokp_url: ${env.RH_SERVER_OKP}
117-
offline: true
123+
retrieval:
124+
inline:
125+
sources:
126+
- okp
127+
max_chunks: 3 # Final tight cap on chunks returned to user
128+
okp:
129+
rhokp_url: ${env.RH_SERVER_OKP}
130+
offline: true
131+
max_chunks: 5 # Initial generous fetch from OKP
118132
```
119133
120-
Tool RAG:
134+
**Tool RAG:**
121135
122136
```yaml
123137
# RAG configuration
124138
rag:
125-
tool:
126-
- okp
127-
okp:
128-
rhokp_url: ${env.RH_SERVER_OKP}
129-
offline: true
139+
retrieval:
140+
tool:
141+
sources:
142+
- okp
143+
max_chunks: 10 # Tool RAG limit
144+
okp:
145+
rhokp_url: ${env.RH_SERVER_OKP}
146+
offline: true
147+
max_chunks: 5
130148
```
131149
132-
* **`rag.inline`** and **`rag.tool`**: Enable OKP as the RAG source for inline context injection and for the RAG tool. Tool rag means the LLM will be provided a search tool it can choose to invoke to find relevant content and augment the user prompt. The tool may or may not be invoked. Inline means a rag search and prompt augmentation will always occur.
150+
* **`rag.retrieval.inline`** and **`rag.retrieval.tool`**: Enable OKP as the RAG source for inline context injection and for the RAG tool. Tool RAG means the LLM will be provided a search tool it can choose to invoke to find relevant content and augment the user prompt. The tool may or may not be invoked. Inline RAG means a RAG search and prompt augmentation will always occur.
133151
* **`okp.offline`**: When `true`, source URLs use `parent_id` (offline/Mimir-style). When `false`, use `reference_url` (online).
152+
* **`okp.search_mode`**: Default Solr search strategy for OKP queries. Controls *how* Solr ranks chunks, not *which* documents are eligible (that is `chunk_filter_query` and per-request `solr.filters`). Allowed values:
153+
- **`hybrid`** (default when the field is omitted): combines vector similarity with keyword (BM25) text search. Requires an embedding model. This is the global default.
154+
- **`semantic`**: pure vector search against Solr embeddings. Requires an embedding model. Best when the query is conceptual rather than matching document wording.
155+
- **`keyword`**: BM25 text search only. No embedding model is needed, so it is the right choice for air-gapped or embedding-free deployments, and it is typically faster than hybrid.
156+
* **`max_chunks` behavior**:
157+
- **Inline RAG**: `rag.retrieval.inline.max_chunks` is the final tight cap on chunks returned in the response, while `rag.okp.max_chunks` is the initial generous fetch from OKP before filtering and ranking.
158+
- **Tool RAG**: `rag.retrieval.tool.max_chunks` sets the limit on chunks available to the tool.
134159

135160
If you want to filter the docs to a specific product, you can include a static query filter such as:
136161

137162
```yaml
138163
okp:
139164
offline: true
165+
search_mode: keyword
140166
chunk_filter_query: "product:*openshift* AND product_version:4.21"
141167
```
142168

169+
A per-request `solr.mode` on `/v1/query` (or streaming/responses) overrides this YAML default for that call. Per-request `lexical` is treated as `keyword`.
170+
171+
> **Important:** When running OGX in **Server mode**, changes to static OKP settings such as `chunk_filter_query` and `search_mode` require an OGX restart to take effect. They are applied when Lightspeed Stack enriches OGX configuration at startup and will not propagate automatically during runtime.
172+
143173
When you launch Lightspeed Stack it will augment the OGX configuration (the synthesized run.yaml in unified mode, or your external run.yaml in the deprecated legacy mode) with
144174
configuration for OKP.
145175

@@ -258,7 +288,7 @@ curl -sX POST http://localhost:8080/v1/query \
258288
- **Static filters preserved:** The configured `chunk_filter_query` (e.g., `"product:*openshift*"`) is always applied
259289
- **Dynamic filters added:** Request filters are combined with static filters using AND logic
260290
- **String escaping:** Special Solr characters in filter values are automatically escaped
261-
- **Works with all search modes:** Filters apply to `semantic`, `hybrid`, and `lexical` search modes
291+
- **Works with all search modes:** Filters apply whether the search mode is `semantic`, `hybrid`, or `keyword` (per-request `lexical` is an alias of `keyword`).
262292

263293
---
264294

@@ -312,13 +342,15 @@ Example response excerpt:
312342
],
313343
```
314344

315-
> **Note:** The first time you query the system the response may take
316-
> additional time because it must first download the necessary embedding model
317-
> to perform the vector search.
345+
> **Note:** The first time you query the system, `hybrid` or `semantic` search may take
346+
> additional time because OGX must download the embedding model used for vector
347+
> search. `keyword` search skips that download.
318348

319349
If you see no RAG context, verify:
320350

321351
1. OKP is up at http://localhost:8081
322352
2. `lightspeed-stack.yaml` has `okp` under `rag.inline` and/or `rag.tool` as in Step 4
323353

354+
The Behave suite `okp_rag.feature` (`@cfg_okp`) runs in Konflux in CI. To stand up OKP + OGX + LCS on a laptop and run a matching scenario, see [Running OKP RAG tests locally](../testing/e2e_testing.md#running-okp-rag-tests-locally).
355+
324356
---

0 commit comments

Comments
 (0)