From 4138cd7b911c66a67f6130456149a89f2dbf3963 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Fri, 7 Aug 2026 12:10:31 +0200 Subject: [PATCH 01/16] fix konflux tests --- .../manifests/lightspeed/llama-stack-openai.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml index 11e76dcf8..e08c61eb5 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml @@ -88,6 +88,13 @@ spec: fi cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/src/.llama/storage/rag/kv_store.db cp /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py + # Mirror GH Actions e2e: pre-download the FAISS embedding model into the PVC so + # query-time retrieval can run with HF_HUB_OFFLINE=1 (no HuggingFace egress). + export HF_HOME=/opt/app-root/src/.cache/huggingface + mkdir -p "$HF_HOME" + echo "Pre-downloading sentence-transformers/all-mpnet-base-v2 into $HF_HOME ..." + /opt/app-root/.venv/bin/python -c \ + "from sentence_transformers import SentenceTransformer; SentenceTransformer('sentence-transformers/all-mpnet-base-v2')" chmod -R 775 /opt/app-root && chown -R 1001:0 /opt/app-root volumeMounts: - name: app-root @@ -132,6 +139,11 @@ spec: value: "/opt/app-root/src" - name: HOME value: "/opt/app-root/src" + # Match docker-compose / GH Actions: use the PVC-cached embedding model offline. + - name: HF_HOME + value: "/opt/app-root/src/.cache/huggingface" + - name: HF_HUB_OFFLINE + value: "1" - name: KV_STORE_PATH value: "/opt/app-root/src/.llama/storage/kv_store.db" - name: KV_RAG_PATH From 7c360467c552f1b5796e5e5f417a20c46a4b601d Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Fri, 7 Aug 2026 22:31:29 +0200 Subject: [PATCH 02/16] fix konflux tests --- scripts/e2e_verify_rag_fixture.py | 65 +++++++++++++++++++ .../lightspeed/llama-stack-openai.yaml | 62 ++++++++---------- 2 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 scripts/e2e_verify_rag_fixture.py diff --git a/scripts/e2e_verify_rag_fixture.py b/scripts/e2e_verify_rag_fixture.py new file mode 100644 index 000000000..af3c3fa4a --- /dev/null +++ b/scripts/e2e_verify_rag_fixture.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""Verify the e2e FAISS RAG SQLite fixture before starting Llama Stack. + +Prints [e2e-rag] lines for Konflux/Prow log dumps and exits non-zero when the +fixture is missing the FAISS index or does not match FAISS_VECTOR_STORE_ID. +""" + +from __future__ import annotations + +import os +import sqlite3 +import sys + + +def main() -> int: + """Validate RAG fixture path from env and return a process exit code.""" + path = os.environ.get("RAG_WORK") or os.environ.get("KV_RAG_PATH") + expected = os.environ.get("FAISS_VECTOR_STORE_ID", "") + if not path: + print("FATAL: RAG_WORK or KV_RAG_PATH must be set", file=sys.stderr) + return 1 + if not os.path.isfile(path): + print(f"FATAL: RAG fixture missing: {path}", file=sys.stderr) + return 1 + + size = os.path.getsize(path) + conn = sqlite3.connect(path) + rows = conn.execute( + "SELECT key, length(value) FROM kvstore WHERE key LIKE '%faiss_index%'" + ).fetchall() + vs_keys = conn.execute( + "SELECT key FROM kvstore WHERE key LIKE '%vector_stores:v%::%' " + "AND key NOT LIKE '%openai%' AND key NOT LIKE '%files%'" + ).fetchall() + conn.close() + + print(f"[e2e-rag] fixture={path} size={size}") + print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected!r}") + print(f"[e2e-rag] vector_stores keys={vs_keys}") + print(f"[e2e-rag] faiss_index keys={rows}") + + if size < 1_048_576: + print(f"FATAL: RAG fixture too small: {size}", file=sys.stderr) + return 1 + if not rows: + print("FATAL: no faiss_index key in RAG fixture", file=sys.stderr) + return 1 + + key, val_len = rows[0] + if expected and expected not in key: + print( + f"FATAL: FAISS_VECTOR_STORE_ID {expected!r} not in index key {key!r}", + file=sys.stderr, + ) + return 1 + if val_len < 100_000: + print(f"FATAL: faiss_index value too small: {val_len}", file=sys.stderr) + return 1 + + print("[e2e-rag] FAISS fixture OK") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml index e08c61eb5..350edc3c9 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml @@ -1,8 +1,10 @@ -# Llama Stack from source on UBI: init clones repo + seeds FAISS, main enriches run.yaml and runs Llama. +# Llama Stack from source on UBI: init clones repo + seeds FAISS, main restores seed then uses +# scripts/llama-stack-entrypoint.sh (same enrich/start path as GitHub Actions docker-compose). # Needs ConfigMaps: llama-stack-config (run.yaml), rag-data (kv_store.db.gz), lightspeed-stack-config; # optional llama-stack-source for repo_url / repo_revision. # -# RAG: seeded in setup-from-source from rag-data ConfigMap (gzip); main re-inflates from rag-data mount. +# RAG: seeded in setup-from-source from rag-data ConfigMap (gzip); main re-inflates, verifies the +# FAISS index key, then lets the shared entrypoint copy the fixture into writable KV_STORE_PATH. apiVersion: v1 kind: Pod metadata: @@ -55,6 +57,9 @@ spec: fi cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/src/.llama/storage/rag/kv_store.db fi + cp -f /opt/app-root/scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh + cp -f /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py + chmod 755 /opt/app-root/enrich-entrypoint.sh chmod -R 775 /opt/app-root && chown -R 1001:0 /opt/app-root echo "PVC fast-path complete" exit 0 @@ -87,14 +92,9 @@ spec: exit 1 fi cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/src/.llama/storage/rag/kv_store.db - cp /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py - # Mirror GH Actions e2e: pre-download the FAISS embedding model into the PVC so - # query-time retrieval can run with HF_HUB_OFFLINE=1 (no HuggingFace egress). - export HF_HOME=/opt/app-root/src/.cache/huggingface - mkdir -p "$HF_HOME" - echo "Pre-downloading sentence-transformers/all-mpnet-base-v2 into $HF_HOME ..." - /opt/app-root/.venv/bin/python -c \ - "from sentence_transformers import SentenceTransformer; SentenceTransformer('sentence-transformers/all-mpnet-base-v2')" + cp -f /opt/app-root/scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh + cp -f /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py + chmod 755 /opt/app-root/enrich-entrypoint.sh chmod -R 775 /opt/app-root && chown -R 1001:0 /opt/app-root volumeMounts: - name: app-root @@ -139,17 +139,14 @@ spec: value: "/opt/app-root/src" - name: HOME value: "/opt/app-root/src" - # Match docker-compose / GH Actions: use the PVC-cached embedding model offline. - - name: HF_HOME - value: "/opt/app-root/src/.cache/huggingface" - - name: HF_HUB_OFFLINE - value: "1" + # Match GitHub Actions docker-compose + llama-stack-entrypoint.sh: + # registry/SQL under /tmp (writable); FAISS (run-ci kv_rag) reads the restored fixture. - name: KV_STORE_PATH - value: "/opt/app-root/src/.llama/storage/kv_store.db" + value: "/tmp/llama/kv_store.db" - name: KV_RAG_PATH value: "/opt/app-root/src/.llama/storage/rag/kv_store.db" - name: SQL_STORE_PATH - value: "/opt/app-root/src/.llama/storage/sql_store.db" + value: "/tmp/llama/sql_store.db" - name: OPENAI_API_KEY valueFrom: secretKeyRef: @@ -201,28 +198,23 @@ spec: elif [[ -f "$RAG_SEED" ]]; then cp -f "$RAG_SEED" "$RAG_WORK" chmod 664 "$RAG_WORK" 2>/dev/null || true + else + echo "FATAL: no RAG seed at $RAG_CM_GZ or $RAG_SEED" + exit 1 fi } + # Re-inflate golden FAISS fixture (same bytes GH bind-mounts from tests/e2e/rag). restore_rag_seed - INPUT_CONFIG="${LLAMA_STACK_CONFIG:-/opt/app-root/run.yaml}" - ENRICHED_CONFIG="/opt/app-root/run.yaml" - LIGHTSPEED_CONFIG="${LIGHTSPEED_CONFIG:-/opt/app-root/lightspeed-stack.yaml}" - if [[ -f "$LIGHTSPEED_CONFIG" ]]; then - echo "Enriching llama-stack config..." - ENRICHMENT_FAILED=0 - /opt/app-root/.venv/bin/python3 /opt/app-root/llama_stack_configuration.py \ - -c "$LIGHTSPEED_CONFIG" \ - -i "$INPUT_CONFIG" \ - -o "$ENRICHED_CONFIG" 2>&1 || ENRICHMENT_FAILED=1 - if [[ -f "$ENRICHED_CONFIG" ]] && [[ "$ENRICHMENT_FAILED" -eq 0 ]]; then - echo "Using enriched config: $ENRICHED_CONFIG" - restore_rag_seed - exec ogx stack run "$ENRICHED_CONFIG" - fi + # Fail fast with evidence in pod logs (e2e-ops dumps these on scenario failure). + RAG_WORK="$RAG_WORK" python3 /opt/app-root/scripts/e2e_verify_rag_fixture.py + if [[ ! -x /opt/app-root/enrich-entrypoint.sh ]]; then + echo "FATAL: missing /opt/app-root/enrich-entrypoint.sh (init should install it)" + exit 1 fi - echo "Using original config: $INPUT_CONFIG" - restore_rag_seed - exec ogx stack run "$INPUT_CONFIG" + # Same enrich + ogx start path as GitHub Actions (docker-compose entrypoint). + export LLAMA_STACK_CONFIG="${LLAMA_STACK_CONFIG:-/opt/app-root/run.yaml}" + export LIGHTSPEED_CONFIG="${LIGHTSPEED_CONFIG:-/opt/app-root/lightspeed-stack.yaml}" + exec /opt/app-root/enrich-entrypoint.sh ports: - containerPort: 8321 readinessProbe: From 2509c2365000d3bf414562f1743bf4f4d032367e Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sat, 8 Aug 2026 21:54:05 +0200 Subject: [PATCH 03/16] retrigger konflux --- tests/e2e/features/unified-mode-validation.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/features/unified-mode-validation.feature b/tests/e2e/features/unified-mode-validation.feature index a298e89f2..335dcbb51 100644 --- a/tests/e2e/features/unified-mode-validation.feature +++ b/tests/e2e/features/unified-mode-validation.feature @@ -19,6 +19,7 @@ Feature: Unified mode configuration validation Then the validation error contains --migrate-config + Scenario: config_format_version legacy with unified-shaped body fails at load Given The service uses the lightspeed-stack-invalid-version-legacy-unified-body.yaml configuration When configuration validation is attempted for the active configuration From c639f751e99bf36a06661592ca75a571787ca36b Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 07:32:49 +0200 Subject: [PATCH 04/16] fix profiles --- .konflux/profiles.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.konflux/profiles.toml b/.konflux/profiles.toml index 08cb82147..09aeb7d91 100644 --- a/.konflux/profiles.toml +++ b/.konflux/profiles.toml @@ -7,6 +7,6 @@ bootstrap_packages = ["maturin"] rhoai_index_url = "https://packages.redhat.com/api/pypi/public-rhai/rhoai/3.5/cpu-ubi9/simple/" output_suffix = "" tekton_files = [ - ".tekton/lightspeed-stack-0-7-pull-request.yaml", - ".tekton/lightspeed-stack-0-7-push.yaml", + ".tekton/lightspeed-stack-0-8-pull-request.yaml", + ".tekton/lightspeed-stack-0-8-push.yaml", ] From f44c4c156935f69fc6ab975747f976b5da469078 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 07:35:30 +0200 Subject: [PATCH 05/16] fix --- .konflux/requirements.hashes.wheel.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt index 53692dfc8..859f96e6b 100644 --- a/.konflux/requirements.hashes.wheel.txt +++ b/.konflux/requirements.hashes.wheel.txt @@ -291,9 +291,11 @@ py-key-value-aio==0.4.5 \ --hash=sha256:9a7d2708e89c3262fca6fe38d01239d481846aa30121a0520e449994e7e9a910 pyaml==26.7.0 \ --hash=sha256:667a4b440272f1376fae57023a213a583a707d29bc4127d354ecc3668adefd7a -pyarrow==24.0.0 \ - --hash=sha256:07fa0b8ce4ccf95d56446ec653a9cae31532dffa8a8841b6b1492ba2a46ee7cb \ - --hash=sha256:be6bbfda99fa5c67655c7cd99044bbde1db8319d2b9a2a54bff5dda2f4010dca +pyarrow==25.0.0 \ + --hash=sha256:b7cc7bfc4f74e1b44f9699468c26df0925f75cbef99346ac35efac8c13cb71b5 \ + --hash=sha256:dc55a12747b836dfd27b2055e343c6b00d9f726dec25ad097bbe536140b9bc1f \ + --hash=sha256:4f0260a86a95607e6f1941fd15b352b56be6743c272ba9e2a7ae65d3e63f0f22 \ + --hash=sha256:dd44b035b1fb5305894e7aee13247fb49d3eccd691430cbe1f3ca98b9d3c5377 pyasn1==0.6.3 \ --hash=sha256:6262dfa40ecb9b64fe0a62f791b6b733165ec158cf9f869d568fd8178044013d pyasn1-modules==0.4.2 \ From 347cca96b37ffba96490e9b9809629ff019a0aab Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 17:54:21 +0200 Subject: [PATCH 06/16] use proper pydantic --- .konflux/requirements.hashes.source.txt | 19 ++++++++++++++++--- .konflux/requirements.hashes.wheel.txt | 8 -------- .../lightspeed-stack-0-8-pull-request.yaml | 2 +- .tekton/lightspeed-stack-0-8-push.yaml | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.konflux/requirements.hashes.source.txt b/.konflux/requirements.hashes.source.txt index 29016231d..3e1dbe27a 100644 --- a/.konflux/requirements.hashes.source.txt +++ b/.konflux/requirements.hashes.source.txt @@ -102,9 +102,22 @@ google-cloud-resource-manager==1.18.0 \ oci==2.182.1 \ --hash=sha256:0c616a6bc3bc458464bc3456469d8da63a1a2d2277e9314b41a1c4e76d5df523 \ --hash=sha256:9862de221f2abe9cf8319393eec58ea59c014fd9b61afaf0a3cca163e2a508b0 -pydantic-ai-skills==1.2.0 \ - --hash=sha256:7ff4eb4b307deb6ef3bf31a6d9493c1e3a8e9b866a13b866a9ea4606bd914206 \ - --hash=sha256:e52f7e8243343dfa94206aae11cd142120edb8990fc392b1d8af32f99b6d351e +# Aligned with uv.lock / pyproject (>=2.23); PyPI only — RHOAI index tops out at 2.9.x. +pydantic-ai==2.26.0 \ + --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 \ + --hash=sha256:f04585e1b16047e17bfeda8ce5d5f5b549fa3567a000ee2e1f012ac5cc893ccf +pydantic-ai-skills==1.3.0 \ + --hash=sha256:4a8e001054b8c458d9b9b1d7688f0a30602246473ed8dfbe235dc4557b458dff \ + --hash=sha256:9940240170fa315640b76ec94be430bea1df55ac6d625a285725b4994f07f86d +pydantic-ai-slim==2.26.0 \ + --hash=sha256:855a23f120328e7a12e8f4371db597d74f65925e20ce335d3a6db81203238f58 \ + --hash=sha256:d41a40a976885d5f9c6848552fcd6732d5daa8294faf4d3e0138fb28118b6734 +pydantic-evals==2.26.0 \ + --hash=sha256:41f92eee7270dbb85e6639082256ff14877c1f0bba0d7dda30e683efb6555e0d \ + --hash=sha256:b5bcac364042d18a028b9e747c1f930f171706c23cab50473c769e053b43a4c0 +pydantic-graph==2.26.0 \ + --hash=sha256:4599a980747588faf17ac56cfa9c10b2a79723a5a20c3c7ee479e8366653097c \ + --hash=sha256:12d9da6c5a0e2634d89f2795ca15783ee37250fdc295b33b5c14232576dafdac pythainlp==5.3.4 \ --hash=sha256:76744e51e27c895630bafd74f53a1f0aa8782cef2f7f02eebd6427fe8ce8d84d \ --hash=sha256:e66fd76fb5931834fd4e32ed54337ec62350d7654f187850e4dd4f915e9f624f diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt index 859f96e6b..54bf253d6 100644 --- a/.konflux/requirements.hashes.wheel.txt +++ b/.konflux/requirements.hashes.wheel.txt @@ -304,17 +304,9 @@ pycparser==3.0 \ --hash=sha256:c2e6198bf8dc97ba19b5f42d9d0e08d5842ffb775e0e208b150080dcf423396e pydantic==2.13.4 \ --hash=sha256:1a3d4d45204182df30dbd68890bdf1becec13f689075099866dd4bdf06b04371 -pydantic-ai==2.5.0 \ - --hash=sha256:e2278856e6f3d653601e39ce626a90c99faa139b03c742532da58d64d9f8cab0 -pydantic-ai-slim==2.5.0 \ - --hash=sha256:8436abb3db39526cbc903de3d78c150a28fcc55463db3dfb5a82ee1b5cb27c50 pydantic-core==2.46.4 \ --hash=sha256:38235509e8f37596cc39453972c1a9b200287d92a98eb2a7c92d9708a98eec49 \ --hash=sha256:bbc9884932978a8f52c59aac4a0a273c8e514d303fd040c38b0436b37e57ed33 -pydantic-evals==2.5.0 \ - --hash=sha256:b7d38de7f719dd71f5b91302e7555198d0e19c5d62a7c19908ad5aadfb5e275a -pydantic-graph==2.5.0 \ - --hash=sha256:61ad8c88d52760b864e186e4342da3d5de5e2d8a232f66ac562c69b108889a62 pydantic-settings==2.14.2 \ --hash=sha256:0398c90f6e52de6fc403d1aa3dfd53c7be8e011759e7fcd7b317fffb0649f726 pygments==2.20.0 \ diff --git a/.tekton/lightspeed-stack-0-8-pull-request.yaml b/.tekton/lightspeed-stack-0-8-pull-request.yaml index 3fdd49476..0d461ddc8 100644 --- a/.tekton/lightspeed-stack-0-8-pull-request.yaml +++ b/.tekton/lightspeed-stack-0-8-pull-request.yaml @@ -53,7 +53,7 @@ spec: ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 diff --git a/.tekton/lightspeed-stack-0-8-push.yaml b/.tekton/lightspeed-stack-0-8-push.yaml index 359a64293..423d14c52 100644 --- a/.tekton/lightspeed-stack-0-8-push.yaml +++ b/.tekton/lightspeed-stack-0-8-push.yaml @@ -54,7 +54,7 @@ spec: ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 From 207db6e4777d936a36785cdac7f44e800ce686f8 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 18:09:06 +0200 Subject: [PATCH 07/16] use proper pydantic --- .konflux/requirements.hashes.source.txt | 16 +--------------- .konflux/requirements.hashes.wheel.pypi.txt | 10 ++++++++++ .tekton/lightspeed-stack-0-8-pull-request.yaml | 3 ++- .tekton/lightspeed-stack-0-8-push.yaml | 3 ++- deploy/lightspeed-stack/Containerfile | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.konflux/requirements.hashes.source.txt b/.konflux/requirements.hashes.source.txt index 3e1dbe27a..8cc63c15c 100644 --- a/.konflux/requirements.hashes.source.txt +++ b/.konflux/requirements.hashes.source.txt @@ -102,22 +102,8 @@ google-cloud-resource-manager==1.18.0 \ oci==2.182.1 \ --hash=sha256:0c616a6bc3bc458464bc3456469d8da63a1a2d2277e9314b41a1c4e76d5df523 \ --hash=sha256:9862de221f2abe9cf8319393eec58ea59c014fd9b61afaf0a3cca163e2a508b0 -# Aligned with uv.lock / pyproject (>=2.23); PyPI only — RHOAI index tops out at 2.9.x. -pydantic-ai==2.26.0 \ - --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 \ - --hash=sha256:f04585e1b16047e17bfeda8ce5d5f5b549fa3567a000ee2e1f012ac5cc893ccf pydantic-ai-skills==1.3.0 \ - --hash=sha256:4a8e001054b8c458d9b9b1d7688f0a30602246473ed8dfbe235dc4557b458dff \ - --hash=sha256:9940240170fa315640b76ec94be430bea1df55ac6d625a285725b4994f07f86d -pydantic-ai-slim==2.26.0 \ - --hash=sha256:855a23f120328e7a12e8f4371db597d74f65925e20ce335d3a6db81203238f58 \ - --hash=sha256:d41a40a976885d5f9c6848552fcd6732d5daa8294faf4d3e0138fb28118b6734 -pydantic-evals==2.26.0 \ - --hash=sha256:41f92eee7270dbb85e6639082256ff14877c1f0bba0d7dda30e683efb6555e0d \ - --hash=sha256:b5bcac364042d18a028b9e747c1f930f171706c23cab50473c769e053b43a4c0 -pydantic-graph==2.26.0 \ - --hash=sha256:4599a980747588faf17ac56cfa9c10b2a79723a5a20c3c7ee479e8366653097c \ - --hash=sha256:12d9da6c5a0e2634d89f2795ca15783ee37250fdc295b33b5c14232576dafdac + --hash=sha256:4a8e001054b8c458d9b9b1d7688f0a30602246473ed8dfbe235dc4557b458dff pythainlp==5.3.4 \ --hash=sha256:76744e51e27c895630bafd74f53a1f0aa8782cef2f7f02eebd6427fe8ce8d84d \ --hash=sha256:e66fd76fb5931834fd4e32ed54337ec62350d7654f187850e4dd4f915e9f624f diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt index c1d470df5..3159626cd 100644 --- a/.konflux/requirements.hashes.wheel.pypi.txt +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -1 +1,11 @@ --index-url https://pypi.org/simple +# Aligned with uv.lock / pyproject (>=2.23). Pure PyPI wheels (RHOAI tops out at 2.9.x). +# Wheel-only hashes — hermetic builds cannot install sdist build backends. +pydantic-ai==2.26.0 \ + --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 +pydantic-ai-slim==2.26.0 \ + --hash=sha256:855a23f120328e7a12e8f4371db597d74f65925e20ce335d3a6db81203238f58 +pydantic-evals==2.26.0 \ + --hash=sha256:41f92eee7270dbb85e6639082256ff14877c1f0bba0d7dda30e683efb6555e0d +pydantic-graph==2.26.0 \ + --hash=sha256:4599a980747588faf17ac56cfa9c10b2a79723a5a20c3c7ee479e8366653097c diff --git a/.tekton/lightspeed-stack-0-8-pull-request.yaml b/.tekton/lightspeed-stack-0-8-pull-request.yaml index 0d461ddc8..b1483c7db 100644 --- a/.tekton/lightspeed-stack-0-8-pull-request.yaml +++ b/.tekton/lightspeed-stack-0-8-pull-request.yaml @@ -48,12 +48,13 @@ spec: "path": ".konflux", "requirements_files": [ "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", "requirements.hashes.source.txt", "requirements.hermetic.txt" ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 diff --git a/.tekton/lightspeed-stack-0-8-push.yaml b/.tekton/lightspeed-stack-0-8-push.yaml index 423d14c52..1f141b2ec 100644 --- a/.tekton/lightspeed-stack-0-8-push.yaml +++ b/.tekton/lightspeed-stack-0-8-push.yaml @@ -49,12 +49,13 @@ spec: "path": ".konflux", "requirements_files": [ "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", "requirements.hashes.source.txt", "requirements.hermetic.txt" ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 diff --git a/deploy/lightspeed-stack/Containerfile b/deploy/lightspeed-stack/Containerfile index 8c1ef546f..491839817 100644 --- a/deploy/lightspeed-stack/Containerfile +++ b/deploy/lightspeed-stack/Containerfile @@ -70,7 +70,7 @@ RUN if [ -f /cachi2/cachi2.env ]; then \ . /cachi2/cachi2.env && \ uv venv --seed --no-index --find-links ${PIP_FIND_LINKS} && \ . .venv/bin/activate && \ - pip install --no-cache-dir --ignore-installed --no-index --find-links ${PIP_FIND_LINKS} --no-deps -r requirements.hashes.wheel.txt -r requirements.hashes.source.txt && \ + pip install --no-cache-dir --ignore-installed --no-index --find-links ${PIP_FIND_LINKS} --no-deps -r requirements.hashes.wheel.txt -r requirements.hashes.wheel.pypi.txt -r requirements.hashes.source.txt && \ pip check; \ else \ uv sync --locked --no-dev --group llslibdev; \ From d4b319211fd379c0ccd6c70230aecc4035805345 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 18:44:29 +0200 Subject: [PATCH 08/16] use proper pydantic --- .konflux/requirements.hashes.source.txt | 2 -- .konflux/requirements.hashes.wheel.pypi.txt | 2 ++ .tekton/lightspeed-stack-0-8-pull-request.yaml | 2 +- .tekton/lightspeed-stack-0-8-push.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.konflux/requirements.hashes.source.txt b/.konflux/requirements.hashes.source.txt index 8cc63c15c..ca6a70bfa 100644 --- a/.konflux/requirements.hashes.source.txt +++ b/.konflux/requirements.hashes.source.txt @@ -102,8 +102,6 @@ google-cloud-resource-manager==1.18.0 \ oci==2.182.1 \ --hash=sha256:0c616a6bc3bc458464bc3456469d8da63a1a2d2277e9314b41a1c4e76d5df523 \ --hash=sha256:9862de221f2abe9cf8319393eec58ea59c014fd9b61afaf0a3cca163e2a508b0 -pydantic-ai-skills==1.3.0 \ - --hash=sha256:4a8e001054b8c458d9b9b1d7688f0a30602246473ed8dfbe235dc4557b458dff pythainlp==5.3.4 \ --hash=sha256:76744e51e27c895630bafd74f53a1f0aa8782cef2f7f02eebd6427fe8ce8d84d \ --hash=sha256:e66fd76fb5931834fd4e32ed54337ec62350d7654f187850e4dd4f915e9f624f diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt index 3159626cd..fae2679de 100644 --- a/.konflux/requirements.hashes.wheel.pypi.txt +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -3,6 +3,8 @@ # Wheel-only hashes — hermetic builds cannot install sdist build backends. pydantic-ai==2.26.0 \ --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 +pydantic-ai-skills==1.3.0 \ + --hash=sha256:4a8e001054b8c458d9b9b1d7688f0a30602246473ed8dfbe235dc4557b458dff pydantic-ai-slim==2.26.0 \ --hash=sha256:855a23f120328e7a12e8f4371db597d74f65925e20ce335d3a6db81203238f58 pydantic-evals==2.26.0 \ diff --git a/.tekton/lightspeed-stack-0-8-pull-request.yaml b/.tekton/lightspeed-stack-0-8-pull-request.yaml index b1483c7db..a0c26fb3d 100644 --- a/.tekton/lightspeed-stack-0-8-pull-request.yaml +++ b/.tekton/lightspeed-stack-0-8-pull-request.yaml @@ -54,7 +54,7 @@ spec: ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-ai-skills,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 diff --git a/.tekton/lightspeed-stack-0-8-push.yaml b/.tekton/lightspeed-stack-0-8-push.yaml index 1f141b2ec..d342ade92 100644 --- a/.tekton/lightspeed-stack-0-8-push.yaml +++ b/.tekton/lightspeed-stack-0-8-push.yaml @@ -55,7 +55,7 @@ spec: ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", + "packages": "a2a-sdk,accelerate,aiofile,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anthropic,anyio,argcomplete,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,beartype,cachetools,caio,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,defusedxml,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,exceptiongroup,executing,faiss-cpu,fastapi,fastmcp-slim,fastuuid,filelock,fire,frozenlist,fsspec,genai-prices,google-api-core,google-auth,google-cloud-core,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,griffelib,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpcore2,httpx,httpx-sse,httpx2,huggingface-hub,idna,importlib-metadata,jaraco-classes,jaraco-context,jaraco-functools,jeepney,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,keyring,kubernetes,langdetect,litellm,logfire,logfire-api,markdown-it-py,markupsafe,maturin,mcp,mdurl,more-itertools,mpmath,msal,msal-extensions,multidict,multiprocess,narwhals,networkx,nltk,numpy,oauthlib,ogx,ogx-api,ogx-client,openai,opentelemetry-api,opentelemetry-distro,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-instrumentation-httpx,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,opentelemetry-util-http,oracledb,packaging,pandas,peft,pip,platformdirs,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,py-key-value-aio,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-ai,pydantic-ai-slim,pydantic-ai-skills,pydantic-core,pydantic-evals,pydantic-graph,pydantic-settings,pygments,pyjwt,pyopenssl,pypdf,pyperclip,python-dateutil,python-dotenv,python-multipart,pytz,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,secretstorage,semver,sentence-transformers,sentry-sdk,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,structlog,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,truststore,typer,typing-extensions,typing-inspection,urllib3,uv,uv-build,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp,zstandard", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 From 278638b98631c917fbb88e55a46476f754db0d80 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Sun, 9 Aug 2026 18:54:00 +0200 Subject: [PATCH 09/16] use proper pydantic --- .konflux/requirements.hashes.wheel.pypi.txt | 2 ++ .konflux/requirements.hashes.wheel.txt | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt index fae2679de..8132731e9 100644 --- a/.konflux/requirements.hashes.wheel.pypi.txt +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -1,6 +1,8 @@ --index-url https://pypi.org/simple # Aligned with uv.lock / pyproject (>=2.23). Pure PyPI wheels (RHOAI tops out at 2.9.x). # Wheel-only hashes — hermetic builds cannot install sdist build backends. +genai-prices==0.1.1 \ + --hash=sha256:de2e3d8ea3ca1d0d292025995c598da447a74e94f22cd3342df46941aeb5416b pydantic-ai==2.26.0 \ --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 pydantic-ai-skills==1.3.0 \ diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt index 54bf253d6..ac47df1af 100644 --- a/.konflux/requirements.hashes.wheel.txt +++ b/.konflux/requirements.hashes.wheel.txt @@ -103,8 +103,6 @@ frozenlist==1.8.0 \ --hash=sha256:824190e162bc775e7f6ded9bde5897738987c51548677c213d1ff728b10efe5b fsspec==2026.4.0 \ --hash=sha256:ef0c6ca507776eab560612d5785f243e2e3f6cfe42468aacdce3609353366b5b -genai-prices==0.0.69 \ - --hash=sha256:f01ccb80e2ac9be9b7d978e4cc97a79023ddd1fc796f50eabd379c23687bb901 google-api-core==2.31.0 \ --hash=sha256:2975c76ecb910406ff6f71c62ed09848d0db421b27986413c448cb8766fc827e google-auth==2.55.1 \ From 030edbe7deb127bdb0200761b6fab8f665e68679 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Mon, 10 Aug 2026 06:48:01 +0200 Subject: [PATCH 10/16] use proper pydantic --- .konflux/requirements.hashes.wheel.pypi.txt | 6 +++++- .konflux/requirements.hashes.wheel.txt | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt index 8132731e9..83fdb1c79 100644 --- a/.konflux/requirements.hashes.wheel.pypi.txt +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -1,8 +1,12 @@ --index-url https://pypi.org/simple -# Aligned with uv.lock / pyproject (>=2.23). Pure PyPI wheels (RHOAI tops out at 2.9.x). +# Aligned with uv.lock. Pure PyPI wheels where RHOAI is too old for pydantic-ai 2.26. # Wheel-only hashes — hermetic builds cannot install sdist build backends. genai-prices==0.1.1 \ --hash=sha256:de2e3d8ea3ca1d0d292025995c598da447a74e94f22cd3342df46941aeb5416b +httpx2==2.9.1 \ + --hash=sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a +openai==2.53.0 \ + --hash=sha256:c694ffc747a3c4d1663ef2b07b811315a476164ee5efa3a993967349ebca7618 pydantic-ai==2.26.0 \ --hash=sha256:1d2324407ed6c206b4c21436059c651051b836fa443b14165236ea9e53379c10 pydantic-ai-skills==1.3.0 \ diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt index ac47df1af..33285ad86 100644 --- a/.konflux/requirements.hashes.wheel.txt +++ b/.konflux/requirements.hashes.wheel.txt @@ -144,8 +144,6 @@ httpx==0.28.1 \ --hash=sha256:40b3fef8650d88ced417b05b236427596a6da9117b0f64ef5b0a5d548d20addb httpx-sse==0.4.3 \ --hash=sha256:e163972748a0d3dcc718803a47bfad40fa1fa5a554d192f60ba685163ab2162a -httpx2==2.5.0 \ - --hash=sha256:78cf66523b7294f6cd2f18e8a04f0b46b2fec6bdb294c464ba111ddd44953129 huggingface-hub==1.22.0 \ --hash=sha256:54ae3efe94beb5a7c9eea3e911ca578945f178e1f211644a0d4f92a1b1648dde idna==3.18 \ @@ -220,8 +218,6 @@ numpy==2.3.5 \ --hash=sha256:a08c6c26c26d7530d09ac93bc177593958c2e3ce4a9b5750c14a80329cca157c oauthlib==3.3.1 \ --hash=sha256:c6fbab4f1a77a539f01175e2ea74b9552806bd0a849c70e744fda4ab801031c0 -openai==2.44.0 \ - --hash=sha256:87429e9a4d15b2918a03b040b6330fa03fc175bbf0bc6eaff7ae61c93cd42c53 ogx==1.0.2+rhaiv.0 \ --hash=sha256:52c891af9dfb22f884d2dae150e5e94d04e492f452ec811bbc61ba84257de000 ogx-api==1.0.2+rhaiv.0 \ From 1dd0683570326e62edfb1fccf385b7a16723a2b8 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Mon, 10 Aug 2026 07:39:10 +0200 Subject: [PATCH 11/16] use proper pydantic --- .konflux/requirements.hashes.wheel.pypi.txt | 2 ++ .konflux/requirements.hashes.wheel.txt | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt index 83fdb1c79..f4fd4d73a 100644 --- a/.konflux/requirements.hashes.wheel.pypi.txt +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -3,6 +3,8 @@ # Wheel-only hashes — hermetic builds cannot install sdist build backends. genai-prices==0.1.1 \ --hash=sha256:de2e3d8ea3ca1d0d292025995c598da447a74e94f22cd3342df46941aeb5416b +httpcore2==2.9.1 \ + --hash=sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26 httpx2==2.9.1 \ --hash=sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a openai==2.53.0 \ diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt index 33285ad86..3bf348cbf 100644 --- a/.konflux/requirements.hashes.wheel.txt +++ b/.konflux/requirements.hashes.wheel.txt @@ -138,8 +138,6 @@ hf-xet==1.5.1 \ --hash=sha256:e0b14eda87d8109be4ca52aa5daef4897ed046ff2e558f363be65008c048149e httpcore==1.0.9 \ --hash=sha256:fc0ea63671089523efc6fe94ec49d0b10c9660460cbca6172fc972c1c5f9c0ac -httpcore2==2.5.0 \ - --hash=sha256:1e8550e99da4297cb64d0cd5dd797c11a736a11f6fcbd8ffec350a9a85b39a27 httpx==0.28.1 \ --hash=sha256:40b3fef8650d88ced417b05b236427596a6da9117b0f64ef5b0a5d548d20addb httpx-sse==0.4.3 \ From fc2a86af9b52c823f4243dd5d5809bc9c01c52fc Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Mon, 10 Aug 2026 10:15:01 +0200 Subject: [PATCH 12/16] add more logging --- scripts/e2e_verify_rag_fixture.py | 47 +++++++++++++++++++-- scripts/llama-stack-entrypoint.sh | 17 ++++++++ tests/e2e-prow/rhoai/pipeline-konflux.sh | 52 ++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 8 deletions(-) diff --git a/scripts/e2e_verify_rag_fixture.py b/scripts/e2e_verify_rag_fixture.py index af3c3fa4a..2ba95bff5 100644 --- a/scripts/e2e_verify_rag_fixture.py +++ b/scripts/e2e_verify_rag_fixture.py @@ -2,16 +2,43 @@ """Verify the e2e FAISS RAG SQLite fixture before starting Llama Stack. Prints [e2e-rag] lines for Konflux/Prow log dumps and exits non-zero when the -fixture is missing the FAISS index or does not match FAISS_VECTOR_STORE_ID. +fixture is missing the FAISS index, does not match FAISS_VECTOR_STORE_ID, or +cannot be deserialized to a non-empty FAISS index. """ from __future__ import annotations +import base64 +import io +import json import os import sqlite3 import sys +def _check_deserialized_ntotal(index_blob: str) -> int: + """Deserialize the FAISS index payload and return ``ntotal``. + + Parameters: + index_blob: JSON string stored under the faiss_index KV key. + + Returns: + Number of vectors in the deserialized index. + + Raises: + Exception: If faiss/numpy are unavailable or deserialization fails. + """ + import faiss # pylint: disable=import-outside-toplevel + import numpy as np # pylint: disable=import-outside-toplevel + + data = json.loads(index_blob) + chunks = data.get("chunk_by_index") or {} + buffer = io.BytesIO(base64.b64decode(data["faiss_index"])) + index = faiss.deserialize_index(np.load(buffer, allow_pickle=False)) + print(f"[e2e-rag] chunk_by_index={len(chunks)} ntotal={index.ntotal} dim={index.d}") + return int(index.ntotal) + + def main() -> int: """Validate RAG fixture path from env and return a process exit code.""" path = os.environ.get("RAG_WORK") or os.environ.get("KV_RAG_PATH") @@ -26,7 +53,7 @@ def main() -> int: size = os.path.getsize(path) conn = sqlite3.connect(path) rows = conn.execute( - "SELECT key, length(value) FROM kvstore WHERE key LIKE '%faiss_index%'" + "SELECT key, length(value), value FROM kvstore WHERE key LIKE '%faiss_index%'" ).fetchall() vs_keys = conn.execute( "SELECT key FROM kvstore WHERE key LIKE '%vector_stores:v%::%' " @@ -37,7 +64,10 @@ def main() -> int: print(f"[e2e-rag] fixture={path} size={size}") print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected!r}") print(f"[e2e-rag] vector_stores keys={vs_keys}") - print(f"[e2e-rag] faiss_index keys={rows}") + print( + f"[e2e-rag] faiss_index keys=" + f"{[(key, val_len) for key, val_len, _ in rows]}" + ) if size < 1_048_576: print(f"FATAL: RAG fixture too small: {size}", file=sys.stderr) @@ -46,7 +76,7 @@ def main() -> int: print("FATAL: no faiss_index key in RAG fixture", file=sys.stderr) return 1 - key, val_len = rows[0] + key, val_len, index_blob = rows[0] if expected and expected not in key: print( f"FATAL: FAISS_VECTOR_STORE_ID {expected!r} not in index key {key!r}", @@ -57,6 +87,15 @@ def main() -> int: print(f"FATAL: faiss_index value too small: {val_len}", file=sys.stderr) return 1 + try: + ntotal = _check_deserialized_ntotal(index_blob) + except Exception as exc: # pylint: disable=broad-exception-caught + print(f"FATAL: failed to deserialize FAISS index: {exc}", file=sys.stderr) + return 1 + if ntotal < 1: + print("FATAL: deserialized FAISS index is empty (ntotal=0)", file=sys.stderr) + return 1 + print("[e2e-rag] FAISS fixture OK") return 0 diff --git a/scripts/llama-stack-entrypoint.sh b/scripts/llama-stack-entrypoint.sh index 2ddcfd2e8..581eec125 100755 --- a/scripts/llama-stack-entrypoint.sh +++ b/scripts/llama-stack-entrypoint.sh @@ -19,6 +19,23 @@ if [ -f "$LIGHTSPEED_CONFIG" ]; then if [ -f "$ENRICHED_CONFIG" ] && [ "$ENRICHMENT_FAILED" -eq 0 ]; then echo "Using enriched config: $ENRICHED_CONFIG" + # Evidence for Konflux RAG failures: which SQLite backends BYOK will open. + /opt/app-root/.venv/bin/python3 - <<'PY' || true +import os, yaml +path = "/tmp/enriched-run.yaml" +with open(path, encoding="utf-8") as fh: + cfg = yaml.safe_load(fh) or {} +backends = (cfg.get("storage") or {}).get("backends") or {} +print("[e2e-rag] KV_RAG_PATH=", os.environ.get("KV_RAG_PATH")) +print("[e2e-rag] KV_STORE_PATH=", os.environ.get("KV_STORE_PATH")) +print("[e2e-rag] FAISS_VECTOR_STORE_ID=", os.environ.get("FAISS_VECTOR_STORE_ID")) +for name, backend in backends.items(): + if "rag" in name or "byok" in name or name.startswith("kv_"): + print(f"[e2e-rag] storage.backends[{name}]={backend}") +stores = ((cfg.get("registered_resources") or {}).get("vector_stores")) or [] +for store in stores: + print(f"[e2e-rag] registered vector_store={store}") +PY exec ogx stack run "$ENRICHED_CONFIG" fi fi diff --git a/tests/e2e-prow/rhoai/pipeline-konflux.sh b/tests/e2e-prow/rhoai/pipeline-konflux.sh index c711826ed..21ba0f12a 100755 --- a/tests/e2e-prow/rhoai/pipeline-konflux.sh +++ b/tests/e2e-prow/rhoai/pipeline-konflux.sh @@ -67,8 +67,10 @@ oc get ns "$NAMESPACE" >/dev/null 2>&1 || oc create namespace "$NAMESPACE" create_secret() { local name=$1; shift - log "Creating secret $name..." - oc create secret generic "$name" "$@" -n "$NAMESPACE" 2>/dev/null || log "Secret $name exists" + log "Creating/updating secret $name..." + # Upsert: a stale FAISS_VECTOR_STORE_ID from a prior run in this namespace + # would otherwise leave registration/search pointing at the wrong store. + oc create secret generic "$name" "$@" -n "$NAMESPACE" --dry-run=client -o yaml | oc apply -f - } create_secret openai-api-key-secret --from-literal=key="$OPENAI_API_KEY" @@ -215,7 +217,9 @@ conn.close() fi gzip -c "$RAG_DB_PATH" > /tmp/kv_store.db.gz - oc create configmap rag-data -n "$NAMESPACE" --from-file=kv_store.db.gz=/tmp/kv_store.db.gz + oc create configmap rag-data -n "$NAMESPACE" \ + --from-file=kv_store.db.gz=/tmp/kv_store.db.gz \ + --dry-run=client -o yaml | oc apply -f - rm /tmp/kv_store.db.gz log "✅ RAG data ConfigMap created from $RAG_DB_PATH" else @@ -408,7 +412,47 @@ log "LCS accessible at: http://$E2E_LSC_HOSTNAME:8080" log "Mock JWKS accessible at: http://$E2E_JWKS_HOSTNAME:8000" log "Llama Stack (e2e client hooks) at: http://$E2E_LLAMA_HOSTNAME:$E2E_LLAMA_PORT" - +# Fail fast if FAISS is registered but returns zero chunks (matches prior Konflux RAG mode). +if [[ -n "${FAISS_VECTOR_STORE_ID:-}" ]]; then + progress "Smoke-testing FAISS vector_io.query for $FAISS_VECTOR_STORE_ID" + RAG_SMOKE_OUT="$(mktemp)" + if ! curl -sf -X POST "http://localhost:8321/v1/vector-io/query" \ + -H "Content-Type: application/json" \ + -d "{\"vector_store_id\":\"${FAISS_VECTOR_STORE_ID}\",\"query\":\"What is the title of the article from Paul?\",\"params\":{\"max_chunks\":5,\"mode\":\"vector\"}}" \ + >"$RAG_SMOKE_OUT"; then + echo "❌ FAISS smoke query HTTP failed" | tee /dev/stderr + e2e_echo_pod_logs 250 + rm -f "$RAG_SMOKE_OUT" + exit 1 + fi + RAG_SMOKE_CHUNKS="$(python3 -c " +import json,sys +try: + data=json.load(open(sys.argv[1], encoding='utf-8')) +except Exception as e: + print(f'parse-error:{e}') + sys.exit(0) +chunks=data.get('chunks') or data.get('data') or [] +if isinstance(data, dict) and not chunks: + # QueryChunksResponse shapes vary; count any list-like payload values. + for v in data.values(): + if isinstance(v, list): + chunks=v + break +print(len(chunks) if isinstance(chunks, list) else 0) +" "$RAG_SMOKE_OUT" 2>/dev/null || echo 0)" + log "[e2e-rag] smoke query chunks=${RAG_SMOKE_CHUNKS} body=$(head -c 400 "$RAG_SMOKE_OUT" | tr '\n' ' ')" + if [[ "${RAG_SMOKE_CHUNKS}" =~ ^[0-9]+$ ]] && [[ "${RAG_SMOKE_CHUNKS}" -lt 1 ]]; then + echo "❌ FAISS smoke query returned 0 chunks — RAG e2e would fail" | tee /dev/stderr + e2e_echo_pod_logs 250 + rm -f "$RAG_SMOKE_OUT" + exit 1 + fi + rm -f "$RAG_SMOKE_OUT" + log "✅ FAISS smoke query returned ${RAG_SMOKE_CHUNKS} chunk(s)" +else + log "⚠️ FAISS_VECTOR_STORE_ID unset — skipping RAG smoke query" +fi #======================================== # 7. RUN TESTS From 0570cb59794c7576e86fe2ca060504da5b90d49d Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Mon, 10 Aug 2026 13:35:50 +0200 Subject: [PATCH 13/16] fix kv_store problem --- tests/e2e-prow/rhoai/pipeline-konflux.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/e2e-prow/rhoai/pipeline-konflux.sh b/tests/e2e-prow/rhoai/pipeline-konflux.sh index 21ba0f12a..7f2bb5f7a 100755 --- a/tests/e2e-prow/rhoai/pipeline-konflux.sh +++ b/tests/e2e-prow/rhoai/pipeline-konflux.sh @@ -217,9 +217,12 @@ conn.close() fi gzip -c "$RAG_DB_PATH" > /tmp/kv_store.db.gz + # Do not use `oc apply` here: client-side apply stores the full object in + # metadata.annotations.kubectl.kubernetes.io/last-applied-configuration + # (256KiB limit). The gzipped FAISS fixture (~800KiB+) overflows that. + oc delete configmap rag-data -n "$NAMESPACE" --ignore-not-found oc create configmap rag-data -n "$NAMESPACE" \ - --from-file=kv_store.db.gz=/tmp/kv_store.db.gz \ - --dry-run=client -o yaml | oc apply -f - + --from-file=kv_store.db.gz=/tmp/kv_store.db.gz rm /tmp/kv_store.db.gz log "✅ RAG data ConfigMap created from $RAG_DB_PATH" else From c6f3130865ea1b21b62f5fb1efaf82073cb8f188 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Mon, 10 Aug 2026 21:14:48 +0200 Subject: [PATCH 14/16] debug --- deploy/llama-stack/test.containerfile | 6 +- docker-compose.yaml | 2 + scripts/e2e_verify_enriched_rag_config.py | 229 ++++++++++++++++++ scripts/e2e_verify_rag_fixture.py | 44 +++- scripts/llama-stack-entrypoint.sh | 24 +- .../lightspeed/llama-stack-openai.yaml | 5 +- tests/e2e-prow/rhoai/pipeline-konflux.sh | 24 ++ .../test_e2e_verify_enriched_rag_config.py | 219 +++++++++++++++++ 8 files changed, 529 insertions(+), 24 deletions(-) create mode 100644 scripts/e2e_verify_enriched_rag_config.py create mode 100644 tests/unit/scripts/test_e2e_verify_enriched_rag_config.py diff --git a/deploy/llama-stack/test.containerfile b/deploy/llama-stack/test.containerfile index 92d4d649e..93ea54aaa 100644 --- a/deploy/llama-stack/test.containerfile +++ b/deploy/llama-stack/test.containerfile @@ -42,8 +42,12 @@ RUN mkdir -p /opt/app-root/src/.llama/storage \ # Copy enrichment scripts for runtime config enrichment COPY src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py COPY scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh +COPY scripts/e2e_verify_enriched_rag_config.py /opt/app-root/scripts/e2e_verify_enriched_rag_config.py +COPY scripts/e2e_verify_rag_fixture.py /opt/app-root/scripts/e2e_verify_rag_fixture.py RUN chmod +x /opt/app-root/enrich-entrypoint.sh && \ - chown 1001:0 /opt/app-root/enrich-entrypoint.sh /opt/app-root/llama_stack_configuration.py + chown -R 1001:0 /opt/app-root/enrich-entrypoint.sh \ + /opt/app-root/llama_stack_configuration.py \ + /opt/app-root/scripts # Switch back to the original user USER 1001 diff --git a/docker-compose.yaml b/docker-compose.yaml index 18e43be2d..070c74de5 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,6 +16,8 @@ services: # Host copies so `docker compose up` picks up script changes without rebuilding llama-stack - ./scripts/llama-stack-entrypoint.sh:/opt/app-root/enrich-entrypoint.sh:ro,z - ./src/llama_stack_configuration.py:/opt/app-root/llama_stack_configuration.py:ro,z + - ./scripts/e2e_verify_enriched_rag_config.py:/opt/app-root/scripts/e2e_verify_enriched_rag_config.py:ro,z + - ./scripts/e2e_verify_rag_fixture.py:/opt/app-root/scripts/e2e_verify_rag_fixture.py:ro,z - ${GCP_KEYS_PATH:-./tmp/.gcp-keys-dummy}:/opt/app-root/.gcp-keys:ro - ./lightspeed-stack.yaml:/opt/app-root/lightspeed-stack.yaml:ro,z - llama-storage:/opt/app-root/src/.llama/storage diff --git a/scripts/e2e_verify_enriched_rag_config.py b/scripts/e2e_verify_enriched_rag_config.py new file mode 100644 index 000000000..9aed01e47 --- /dev/null +++ b/scripts/e2e_verify_enriched_rag_config.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +"""Fail-fast checks for OGX-enriched FAISS/BYOK config (Konflux/GH e2e). + +Validates that enrichment produced a usable FAISS setup for the e2e fixture: +namespaced persistence, resolvable SQLite db_path, and a vector_store_id that +expands to FAISS_VECTOR_STORE_ID (not a leftover ``${env....}`` literal). +""" + +from __future__ import annotations + +import os +import re +import sys +from pathlib import Path +from typing import Any + +import yaml + +# OGX uses ``${env.VAR}`` or ``${env.VAR:=default}`` (note ``:=``, not bare ``=``). +_ENV_PATTERN = re.compile( + r"\$\{env\.([A-Za-z_][A-Za-z0-9_]*)(?::=([^}]*))?\}" +) + + +def expand_env_refs(value: str) -> str: + """Expand ``${env.VAR}`` / ``${env.VAR:=default}`` like OGX ``replace_env_vars``. + + Parameters: + value: Raw config string that may contain env references. + + Returns: + String with all env references replaced from the process environment. + """ + + def _replace(match: re.Match[str]) -> str: + name = match.group(1) + default = match.group(2) + env_val = os.environ.get(name) + if env_val is not None and env_val != "": + return env_val + if default is not None: + return default + return "" + + return _ENV_PATTERN.sub(_replace, value) + + +def _byok_backends(backends: dict[str, Any]) -> dict[str, Any]: + """Return storage backends used for BYOK / RAG SQLite.""" + return { + name: backend + for name, backend in backends.items() + if "byok" in name or name in {"kv_rag", "kv_default"} + } + + +def _faiss_providers(vector_io: list[Any]) -> list[dict[str, Any]]: + """Return inline FAISS vector_io provider dicts.""" + out: list[dict[str, Any]] = [] + for provider in vector_io: + if not isinstance(provider, dict): + continue + ptype = str(provider.get("provider_type") or "") + if ptype == "inline::faiss" or str(provider.get("provider_id", "")).startswith( + "byok_" + ): + out.append(provider) + return out + + +def verify_enriched_config(cfg: dict[str, Any]) -> list[str]: + """Return human-readable errors for an enriched Llama/OGX run config. + + Parameters: + cfg: Parsed enriched ``run.yaml`` mapping. + + Returns: + List of error strings (empty means OK). Skips checks when no BYOK/FAISS + providers are present. + """ + errors: list[str] = [] + backends = (cfg.get("storage") or {}).get("backends") or {} + vector_io = ((cfg.get("providers") or {}).get("vector_io")) or [] + if not isinstance(vector_io, list): + vector_io = [] + stores = ((cfg.get("registered_resources") or {}).get("vector_stores")) or [] + if not isinstance(stores, list): + stores = [] + + faiss_providers = _faiss_providers(vector_io) + byok_backends = { + name: backend + for name, backend in backends.items() + if isinstance(name, str) and name.startswith("byok_") + } + + if not faiss_providers and not byok_backends: + print("[e2e-rag] enriched config: no BYOK/FAISS providers — skipping") + return [] + + expected_id = os.environ.get("FAISS_VECTOR_STORE_ID", "").strip() + kv_rag_path = os.environ.get("KV_RAG_PATH", "").strip() + + print(f"[e2e-rag] KV_RAG_PATH={kv_rag_path!r}") + print(f"[e2e-rag] KV_STORE_PATH={os.environ.get('KV_STORE_PATH')!r}") + print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected_id!r}") + + for name, backend in _byok_backends(backends).items(): + print(f"[e2e-rag] storage.backends[{name}]={backend}") + + for provider in faiss_providers: + print(f"[e2e-rag] vector_io provider={provider}") + persistence = (provider.get("config") or {}).get("persistence") or {} + namespace = persistence.get("namespace") + backend_name = persistence.get("backend") + if namespace != "vector_io::faiss": + errors.append( + f"provider {provider.get('provider_id')!r} persistence.namespace " + f"is {namespace!r}, expected 'vector_io::faiss'" + ) + if str(provider.get("provider_id", "")).startswith("byok_"): + if not backend_name or not str(backend_name).startswith("byok_"): + errors.append( + f"BYOK provider {provider.get('provider_id')!r} " + f"persistence.backend is {backend_name!r}" + ) + elif backend_name not in backends: + errors.append( + f"BYOK persistence.backend {backend_name!r} missing from " + "storage.backends" + ) + + if not byok_backends and any( + str(p.get("provider_id", "")).startswith("byok_") for p in faiss_providers + ): + errors.append("BYOK FAISS providers present but no byok_* storage backends") + + for name, backend in byok_backends.items(): + if not isinstance(backend, dict): + errors.append(f"storage.backends[{name}] is not a mapping") + continue + raw_path = str(backend.get("db_path") or "") + resolved = expand_env_refs(raw_path) + print(f"[e2e-rag] resolved db_path[{name}]={resolved!r} (raw={raw_path!r})") + if "${env." in resolved: + errors.append( + f"storage.backends[{name}].db_path still has unresolved env refs: " + f"{resolved!r}" + ) + continue + if not resolved: + errors.append(f"storage.backends[{name}].db_path resolved empty") + continue + # HOME-relative defaults from run-ci.yaml + path = Path(resolved).expanduser() + if not path.is_file(): + errors.append(f"storage.backends[{name}].db_path does not exist: {path}") + continue + size = path.stat().st_size + print(f"[e2e-rag] db_path[{name}] size={size}") + if size < 1_048_576: + errors.append( + f"storage.backends[{name}].db_path too small ({size} bytes): {path}" + ) + if kv_rag_path and path.resolve() != Path(kv_rag_path).expanduser().resolve(): + # Warn in logs; only error when FAISS id is set (e2e fixture path). + if expected_id: + errors.append( + f"storage.backends[{name}].db_path {path} != KV_RAG_PATH " + f"{kv_rag_path}" + ) + + if not stores: + errors.append("registered_resources.vector_stores is empty after enrichment") + for store in stores: + print(f"[e2e-rag] registered vector_store={store}") + if not isinstance(store, dict): + errors.append(f"vector_store entry is not a mapping: {store!r}") + continue + raw_id = str(store.get("vector_store_id") or "") + resolved_id = expand_env_refs(raw_id) + print( + f"[e2e-rag] resolved vector_store_id={resolved_id!r} (raw={raw_id!r})" + ) + if "${env." in resolved_id or not resolved_id: + errors.append( + f"vector_store_id did not expand (raw={raw_id!r}, " + f"resolved={resolved_id!r}); is FAISS_VECTOR_STORE_ID set?" + ) + elif expected_id and resolved_id != expected_id: + errors.append( + f"vector_store_id {resolved_id!r} != FAISS_VECTOR_STORE_ID " + f"{expected_id!r}" + ) + + if expected_id and not expected_id.startswith("vs_"): + errors.append( + f"FAISS_VECTOR_STORE_ID looks invalid for OGX fixture: {expected_id!r}" + ) + + return errors + + +def main(argv: list[str] | None = None) -> int: + """CLI entry: verify enriched run.yaml path (default ``/tmp/enriched-run.yaml``).""" + args = list(sys.argv[1:] if argv is None else argv) + path = args[0] if args else "/tmp/enriched-run.yaml" + if not os.path.isfile(path): + print(f"FATAL: enriched config missing: {path}", file=sys.stderr) + return 1 + + with open(path, encoding="utf-8") as fh: + cfg = yaml.safe_load(fh) or {} + if not isinstance(cfg, dict): + print(f"FATAL: enriched config is not a mapping: {path}", file=sys.stderr) + return 1 + + print(f"[e2e-rag] verifying enriched config={path}") + errors = verify_enriched_config(cfg) + if errors: + for err in errors: + print(f"FATAL: {err}", file=sys.stderr) + return 1 + print("[e2e-rag] enriched FAISS/BYOK config OK") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/e2e_verify_rag_fixture.py b/scripts/e2e_verify_rag_fixture.py index 2ba95bff5..adaab77b1 100644 --- a/scripts/e2e_verify_rag_fixture.py +++ b/scripts/e2e_verify_rag_fixture.py @@ -61,6 +61,15 @@ def main() -> int: ).fetchall() conn.close() + namespaced_index = [ + (key, val_len, blob) + for key, val_len, blob in rows + if key.startswith("vector_io::faiss:faiss_index:") + ] + namespaced_vs = [ + key for (key,) in vs_keys if key.startswith("vector_io::faiss:vector_stores:") + ] + print(f"[e2e-rag] fixture={path} size={size}") print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected!r}") print(f"[e2e-rag] vector_stores keys={vs_keys}") @@ -68,6 +77,11 @@ def main() -> int: f"[e2e-rag] faiss_index keys=" f"{[(key, val_len) for key, val_len, _ in rows]}" ) + print(f"[e2e-rag] namespaced vector_stores={namespaced_vs}") + print( + f"[e2e-rag] namespaced faiss_index=" + f"{[(key, val_len) for key, val_len, _ in namespaced_index]}" + ) if size < 1_048_576: print(f"FATAL: RAG fixture too small: {size}", file=sys.stderr) @@ -75,14 +89,36 @@ def main() -> int: if not rows: print("FATAL: no faiss_index key in RAG fixture", file=sys.stderr) return 1 - - key, val_len, index_blob = rows[0] - if expected and expected not in key: + if not namespaced_index: print( - f"FATAL: FAISS_VECTOR_STORE_ID {expected!r} not in index key {key!r}", + "FATAL: no OGX 1.0 namespaced faiss_index key " + "(expected prefix vector_io::faiss:faiss_index:)", file=sys.stderr, ) return 1 + if not namespaced_vs: + print( + "FATAL: no OGX 1.0 namespaced vector_stores key " + "(expected prefix vector_io::faiss:vector_stores:)", + file=sys.stderr, + ) + return 1 + + key, val_len, index_blob = namespaced_index[0] + if expected: + matched = [ + (k, length, blob) + for k, length, blob in namespaced_index + if expected in k + ] + if not matched: + print( + f"FATAL: FAISS_VECTOR_STORE_ID {expected!r} not in any " + f"namespaced index key {[k for k, _, _ in namespaced_index]!r}", + file=sys.stderr, + ) + return 1 + key, val_len, index_blob = matched[0] if val_len < 100_000: print(f"FATAL: faiss_index value too small: {val_len}", file=sys.stderr) return 1 diff --git a/scripts/llama-stack-entrypoint.sh b/scripts/llama-stack-entrypoint.sh index 581eec125..3af5c6714 100755 --- a/scripts/llama-stack-entrypoint.sh +++ b/scripts/llama-stack-entrypoint.sh @@ -7,6 +7,7 @@ set -e INPUT_CONFIG="${LLAMA_STACK_CONFIG:-/opt/app-root/run.yaml}" ENRICHED_CONFIG="/tmp/enriched-run.yaml" LIGHTSPEED_CONFIG="${LIGHTSPEED_CONFIG:-/opt/app-root/lightspeed-stack.yaml}" +VERIFY_ENRICHED="${E2E_VERIFY_ENRICHED_RAG_CONFIG:-/opt/app-root/scripts/e2e_verify_enriched_rag_config.py}" # Enrich config if lightspeed config exists if [ -f "$LIGHTSPEED_CONFIG" ]; then @@ -19,23 +20,12 @@ if [ -f "$LIGHTSPEED_CONFIG" ]; then if [ -f "$ENRICHED_CONFIG" ] && [ "$ENRICHMENT_FAILED" -eq 0 ]; then echo "Using enriched config: $ENRICHED_CONFIG" - # Evidence for Konflux RAG failures: which SQLite backends BYOK will open. - /opt/app-root/.venv/bin/python3 - <<'PY' || true -import os, yaml -path = "/tmp/enriched-run.yaml" -with open(path, encoding="utf-8") as fh: - cfg = yaml.safe_load(fh) or {} -backends = (cfg.get("storage") or {}).get("backends") or {} -print("[e2e-rag] KV_RAG_PATH=", os.environ.get("KV_RAG_PATH")) -print("[e2e-rag] KV_STORE_PATH=", os.environ.get("KV_STORE_PATH")) -print("[e2e-rag] FAISS_VECTOR_STORE_ID=", os.environ.get("FAISS_VECTOR_STORE_ID")) -for name, backend in backends.items(): - if "rag" in name or "byok" in name or name.startswith("kv_"): - print(f"[e2e-rag] storage.backends[{name}]={backend}") -stores = ((cfg.get("registered_resources") or {}).get("vector_stores")) or [] -for store in stores: - print(f"[e2e-rag] registered vector_store={store}") -PY + # Fail fast when BYOK/FAISS enrichment is wrong for OGX 1.0 (namespace / env / path). + if [ -f "$VERIFY_ENRICHED" ]; then + /opt/app-root/.venv/bin/python3 "$VERIFY_ENRICHED" "$ENRICHED_CONFIG" + else + echo "[e2e-rag] WARNING: missing $VERIFY_ENRICHED — skipping enriched config check" + fi exec ogx stack run "$ENRICHED_CONFIG" fi fi diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml index 350edc3c9..3f3f1e62e 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml @@ -4,7 +4,8 @@ # optional llama-stack-source for repo_url / repo_revision. # # RAG: seeded in setup-from-source from rag-data ConfigMap (gzip); main re-inflates, verifies the -# FAISS index key, then lets the shared entrypoint copy the fixture into writable KV_STORE_PATH. +# OGX 1.0 namespaced FAISS index at KV_RAG_PATH, then enrich-entrypoint checks BYOK backends +# (persistence.namespace + resolved db_path / vector_store_id) before `ogx stack run`. apiVersion: v1 kind: Pod metadata: @@ -140,7 +141,7 @@ spec: - name: HOME value: "/opt/app-root/src" # Match GitHub Actions docker-compose + llama-stack-entrypoint.sh: - # registry/SQL under /tmp (writable); FAISS (run-ci kv_rag) reads the restored fixture. + # registry/SQL under /tmp (writable); FAISS BYOK / kv_rag reads the restored fixture. - name: KV_STORE_PATH value: "/tmp/llama/kv_store.db" - name: KV_RAG_PATH diff --git a/tests/e2e-prow/rhoai/pipeline-konflux.sh b/tests/e2e-prow/rhoai/pipeline-konflux.sh index 7f2bb5f7a..90914906e 100755 --- a/tests/e2e-prow/rhoai/pipeline-konflux.sh +++ b/tests/e2e-prow/rhoai/pipeline-konflux.sh @@ -278,6 +278,30 @@ if ! oc wait pod/lightspeed-stack-service pod/llama-stack-service \ fi log "✅ Both service pods are ready" +# OGX 1.0 / BYOK evidence: fail before port-forward if fixture or enriched config is wrong. +progress "Verifying live RAG fixture + enriched FAISS config in llama-stack pod" +echo "[e2e] ========== llama-stack [e2e-rag] startup lines ==========" +oc logs llama-stack-service -n "$NAMESPACE" 2>&1 | grep '\[e2e-rag\]' \ + | while IFS= read -r line || [[ -n "$line" ]]; do echo "[e2e] $line"; done || true +if ! oc exec llama-stack-service -n "$NAMESPACE" -- /bin/bash -c ' + set -e + export RAG_WORK="${KV_RAG_PATH:-/opt/app-root/src/.llama/storage/rag/kv_store.db}" + echo "[e2e-rag] live KV_RAG_PATH=${KV_RAG_PATH:-}" + echo "[e2e-rag] live FAISS_VECTOR_STORE_ID=${FAISS_VECTOR_STORE_ID:-}" + python3 /opt/app-root/scripts/e2e_verify_rag_fixture.py + if [[ -f /tmp/enriched-run.yaml ]]; then + python3 /opt/app-root/scripts/e2e_verify_enriched_rag_config.py /tmp/enriched-run.yaml + else + echo "FATAL: /tmp/enriched-run.yaml missing after llama-stack start" >&2 + exit 1 + fi +'; then + echo "❌ Live RAG / enriched config verification failed" | tee /dev/stderr + e2e_echo_pod_logs 250 + exit 1 +fi +log "✅ Live RAG fixture + enriched FAISS/BYOK config OK" + if [ "$QUIET" = "1" ]; then e2e_echo_pod_logs 80 else diff --git a/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py b/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py new file mode 100644 index 000000000..75d325b05 --- /dev/null +++ b/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py @@ -0,0 +1,219 @@ +"""Unit tests for scripts/e2e_verify_enriched_rag_config.py.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path + +import pytest + +SCRIPT = ( + Path(__file__).resolve().parents[3] + / "scripts" + / "e2e_verify_enriched_rag_config.py" +) + + +def _load_module(): + """Load the verify script as a module without installing the package.""" + spec = importlib.util.spec_from_file_location( + "e2e_verify_enriched_rag_config", SCRIPT + ) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +@pytest.fixture(name="verify_mod") +def verify_mod_fixture(): + """Import the enriched-config verifier script.""" + return _load_module() + + +def test_expand_env_refs_uses_env_and_default(verify_mod, monkeypatch): + """Env expansion matches OGX ``${env.VAR:=default}`` behavior.""" + monkeypatch.setenv("KV_RAG_PATH", "/tmp/fixture.db") + monkeypatch.delenv("MISSING_VAR", raising=False) + assert ( + verify_mod.expand_env_refs("${env.KV_RAG_PATH:=/unused}") == "/tmp/fixture.db" + ) + assert verify_mod.expand_env_refs("${env.MISSING_VAR:=fallback}") == "fallback" + + +def test_verify_enriched_config_ok(verify_mod, tmp_path, monkeypatch): + """Happy path: namespaced BYOK FAISS + existing fixture DB.""" + db = tmp_path / "kv_store.db" + db.write_bytes(b"x" * 1_100_000) + monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") + monkeypatch.setenv("KV_RAG_PATH", str(db)) + + cfg = { + "storage": { + "backends": { + "kv_rag": { + "type": "kv_sqlite", + "db_path": "${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db}", + }, + "byok_e2e-test-docs_storage": { + "type": "kv_sqlite", + "db_path": "${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db}", + }, + } + }, + "providers": { + "vector_io": [ + { + "provider_id": "faiss", + "provider_type": "inline::faiss", + "config": { + "persistence": { + "namespace": "vector_io::faiss", + "backend": "kv_rag", + } + }, + }, + { + "provider_id": "byok_e2e-test-docs", + "provider_type": "inline::faiss", + "config": { + "persistence": { + "namespace": "vector_io::faiss", + "backend": "byok_e2e-test-docs_storage", + } + }, + }, + ] + }, + "registered_resources": { + "vector_stores": [ + { + "vector_store_id": "${env.FAISS_VECTOR_STORE_ID}", + "provider_id": "byok_e2e-test-docs", + "embedding_model": "sentence-transformers/all-mpnet-base-v2", + "embedding_dimension": 768, + } + ] + }, + } + + assert verify_mod.verify_enriched_config(cfg) == [] + + +def test_verify_enriched_config_rejects_wrong_namespace( + verify_mod, tmp_path, monkeypatch +): + """Missing OGX persistence.namespace must fail.""" + db = tmp_path / "kv_store.db" + db.write_bytes(b"x" * 1_100_000) + monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") + monkeypatch.setenv("KV_RAG_PATH", str(db)) + + cfg = { + "storage": { + "backends": { + "byok_e2e-test-docs_storage": { + "type": "kv_sqlite", + "db_path": str(db), + } + } + }, + "providers": { + "vector_io": [ + { + "provider_id": "byok_e2e-test-docs", + "provider_type": "inline::faiss", + "config": { + "persistence": { + "namespace": "wrong", + "backend": "byok_e2e-test-docs_storage", + } + }, + } + ] + }, + "registered_resources": { + "vector_stores": [{"vector_store_id": "vs_abc"}] + }, + } + + errors = verify_mod.verify_enriched_config(cfg) + assert any("persistence.namespace" in err for err in errors) + + +def test_verify_enriched_config_rejects_unexpanded_store_id( + verify_mod, tmp_path, monkeypatch +): + """Unset FAISS_VECTOR_STORE_ID leaves a literal env ref — must fail.""" + db = tmp_path / "kv_store.db" + db.write_bytes(b"x" * 1_100_000) + monkeypatch.delenv("FAISS_VECTOR_STORE_ID", raising=False) + monkeypatch.setenv("KV_RAG_PATH", str(db)) + + cfg = { + "storage": { + "backends": { + "byok_e2e-test-docs_storage": { + "type": "kv_sqlite", + "db_path": str(db), + } + } + }, + "providers": { + "vector_io": [ + { + "provider_id": "byok_e2e-test-docs", + "provider_type": "inline::faiss", + "config": { + "persistence": { + "namespace": "vector_io::faiss", + "backend": "byok_e2e-test-docs_storage", + } + }, + } + ] + }, + "registered_resources": { + "vector_stores": [ + {"vector_store_id": "${env.FAISS_VECTOR_STORE_ID}"} + ] + }, + } + + errors = verify_mod.verify_enriched_config(cfg) + assert any("did not expand" in err for err in errors) + + +def test_main_ok_with_temp_yaml(verify_mod, tmp_path, monkeypatch): + """CLI main returns 0 for a valid enriched YAML file.""" + db = tmp_path / "kv_store.db" + db.write_bytes(b"x" * 1_100_000) + monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") + monkeypatch.setenv("KV_RAG_PATH", str(db)) + + cfg_path = tmp_path / "enriched-run.yaml" + cfg_path.write_text( + f""" +storage: + backends: + byok_e2e-test-docs_storage: + type: kv_sqlite + db_path: {db} +providers: + vector_io: + - provider_id: byok_e2e-test-docs + provider_type: inline::faiss + config: + persistence: + namespace: vector_io::faiss + backend: byok_e2e-test-docs_storage +registered_resources: + vector_stores: + - vector_store_id: vs_abc +""", + encoding="utf-8", + ) + + assert verify_mod.main([str(cfg_path)]) == 0 From e30ca0f36c462fb6d50dd1fc84ce671bd6744f64 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Tue, 11 Aug 2026 08:20:51 +0200 Subject: [PATCH 15/16] fix --- docker-compose.yaml | 2 ++ tests/e2e-prow/rhoai/configs/run.yaml | 7 +++--- .../lightspeed/lightspeed-stack.yaml | 3 ++- .../lightspeed/llama-stack-openai.yaml | 24 +++++++++++++------ .../lightspeed/llama-stack-prow.yaml | 9 ++++++- tests/e2e-prow/rhoai/pipeline-konflux.sh | 4 +++- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 070c74de5..15e504676 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -59,6 +59,8 @@ services: - OGX_LOGGING=${OGX_LOGGING:-} # FAISS test - FAISS_VECTOR_STORE_ID=${FAISS_VECTOR_STORE_ID:-} + # Disable OGX ~/.llama → ~/.ogx migration (bind-mount under ~/.llama/storage/rag). + - OGX_CONFIG_DIR=/opt/app-root/src/.ogx # Prevent HuggingFace Hub update checks (HTTP 429 rate-limiting in CI from parallel jobs). - HF_HUB_OFFLINE=1 # OKP/Solr RAG diff --git a/tests/e2e-prow/rhoai/configs/run.yaml b/tests/e2e-prow/rhoai/configs/run.yaml index ec50feabb..1e10cfd50 100644 --- a/tests/e2e-prow/rhoai/configs/run.yaml +++ b/tests/e2e-prow/rhoai/configs/run.yaml @@ -63,12 +63,13 @@ server: port: 8321 storage: backends: - kv_default: # Single database for registry AND RAG data + kv_default: type: kv_sqlite - db_path: /opt/app-root/src/.llama/storage/rag/kv_store.db + db_path: ${env.KV_STORE_PATH:=/opt/app-root/src/.llama/storage/kv_store.db} kv_rag: type: kv_sqlite - db_path: /opt/app-root/src/.llama/storage/rag/kv_store.db + # Requires OGX_CONFIG_DIR so migrate_legacy_config_dir() does not move ~/.llama. + db_path: ${env.KV_RAG_PATH:=/opt/app-root/src/.llama/storage/rag/kv_store.db} sql_default: type: sql_sqlite db_path: ${env.SQL_STORE_PATH:=/opt/app-root/src/.llama/storage/sql_store.db} diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/lightspeed-stack.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/lightspeed-stack.yaml index f8aa35caf..d6ed459c4 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/lightspeed-stack.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/lightspeed-stack.yaml @@ -54,8 +54,9 @@ spec: name: faiss-vector-store-secret key: id optional: true + # Unused for server-mode FAISS (llama pod owns the fixture); keep out of ~/.llama. - name: KV_RAG_PATH - value: "/app-root/src/.llama/storage/rag/kv_store.db" + value: "/app-root/.e2e-rag-work/kv_store.db" - name: VLLM_MODEL valueFrom: secretKeyRef: diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml index 3f3f1e62e..da5f18004 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml @@ -6,6 +6,10 @@ # RAG: seeded in setup-from-source from rag-data ConfigMap (gzip); main re-inflates, verifies the # OGX 1.0 namespaced FAISS index at KV_RAG_PATH, then enrich-entrypoint checks BYOK backends # (persistence.namespace + resolved db_path / vector_store_id) before `ogx stack run`. +# +# KV_RAG_PATH must NOT live under ~/.llama: OGX migrate_legacy_config_dir() moves ~/.llama → ~/.ogx +# on startup when OGX_CONFIG_DIR is unset, which orphans the fixture path; SQLite then recreates an +# empty 12KiB DB and register_resources writes only vector_stores metadata (0 chunks). apiVersion: v1 kind: Pod metadata: @@ -48,7 +52,7 @@ spec: && /opt/app-root/.venv/bin/python --version >/dev/null 2>&1 \ && [[ -d /opt/app-root/src ]]; then echo "PVC cache hit: app-root already provisioned — skipping full install" - mkdir -p /opt/app-root/.e2e-rag-seed /opt/app-root/src/.llama/storage/rag /opt/app-root/src/.llama/storage/files + mkdir -p /opt/app-root/.e2e-rag-seed /opt/app-root/.e2e-rag-work /opt/app-root/src/.ogx /opt/app-root/src/.llama/storage/files if [[ -f /rag-seed/kv_store.db.gz ]]; then gzip -dc /rag-seed/kv_store.db.gz > /opt/app-root/.e2e-rag-seed/kv_store.db _sz=$(stat -c%s /opt/app-root/.e2e-rag-seed/kv_store.db) @@ -56,7 +60,7 @@ spec: echo "FATAL: RAG seed too small (${_sz} bytes); check rag-data ConfigMap" exit 1 fi - cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/src/.llama/storage/rag/kv_store.db + cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/.e2e-rag-work/kv_store.db fi cp -f /opt/app-root/scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh cp -f /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py @@ -80,7 +84,7 @@ spec: (cd /opt/app-root/repo && tar cf - .) | (cd /opt/app-root && tar xf -) rm -rf /opt/app-root/repo sed -i 's|/opt/app-root/repo/.venv|/opt/app-root/.venv|g' /opt/app-root/.venv/bin/* 2>/dev/null || true - mkdir -p /opt/app-root/.e2e-rag-seed /opt/app-root/src/.llama/storage/rag /opt/app-root/src/.llama/storage/files + mkdir -p /opt/app-root/.e2e-rag-seed /opt/app-root/.e2e-rag-work /opt/app-root/src/.ogx /opt/app-root/src/.llama/storage/files if [[ ! -f /rag-seed/kv_store.db.gz ]]; then echo "FATAL: missing /rag-seed/kv_store.db.gz (ConfigMap rag-data key kv_store.db.gz)" ls -la /rag-seed || true @@ -92,7 +96,7 @@ spec: echo "FATAL: RAG seed too small (${_sz} bytes); check rag-data ConfigMap" exit 1 fi - cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/src/.llama/storage/rag/kv_store.db + cp -f /opt/app-root/.e2e-rag-seed/kv_store.db /opt/app-root/.e2e-rag-work/kv_store.db cp -f /opt/app-root/scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh cp -f /opt/app-root/src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py chmod 755 /opt/app-root/enrich-entrypoint.sh @@ -141,11 +145,14 @@ spec: - name: HOME value: "/opt/app-root/src" # Match GitHub Actions docker-compose + llama-stack-entrypoint.sh: - # registry/SQL under /tmp (writable); FAISS BYOK / kv_rag reads the restored fixture. + # registry/SQL under /tmp (writable); FAISS BYOK reads restored fixture outside ~/.llama + # so OGX migrate_legacy_config_dir() cannot move it away on startup. + - name: OGX_CONFIG_DIR + value: "/opt/app-root/src/.ogx" - name: KV_STORE_PATH value: "/tmp/llama/kv_store.db" - name: KV_RAG_PATH - value: "/opt/app-root/src/.llama/storage/rag/kv_store.db" + value: "/opt/app-root/.e2e-rag-work/kv_store.db" - name: SQL_STORE_PATH value: "/tmp/llama/sql_store.db" - name: OPENAI_API_KEY @@ -191,7 +198,10 @@ spec: set -e RAG_SEED="/opt/app-root/.e2e-rag-seed/kv_store.db" RAG_CM_GZ="/opt/app-root/rag-data-cm/kv_store.db.gz" - RAG_WORK="${KV_RAG_PATH:-/opt/app-root/src/.llama/storage/rag/kv_store.db}" + RAG_WORK="${KV_RAG_PATH:-/opt/app-root/.e2e-rag-work/kv_store.db}" + # Skip OGX ~/.llama → ~/.ogx migration (would steal a fixture under ~/.llama). + export OGX_CONFIG_DIR="${OGX_CONFIG_DIR:-/opt/app-root/src/.ogx}" + mkdir -p "$OGX_CONFIG_DIR" "$(dirname "$RAG_WORK")" restore_rag_seed() { mkdir -p "$(dirname "$RAG_WORK")" if [[ -f "$RAG_CM_GZ" ]]; then diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-prow.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-prow.yaml index 271304cbd..aae24181e 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-prow.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-prow.yaml @@ -30,13 +30,15 @@ spec: - -c - | set -e - mkdir -p /data/src/.llama/storage/rag /data/src/.llama/storage/files /data/.e2e-rag-seed + mkdir -p /data/src/.llama/storage/rag /data/src/.llama/storage/files /data/src/.ogx /data/.e2e-rag-seed if [ ! -f /rag-data/kv_store.db.gz ]; then echo "FATAL: missing /rag-data/kv_store.db.gz" ls -la /rag-data || true exit 1 fi gunzip -c /rag-data/kv_store.db.gz > /data/.e2e-rag-seed/kv_store.db + # Fixture stays on the emptyDir (mounted at .llama/storage in the main container). + # OGX_CONFIG_DIR must be set so migrate_legacy_config_dir() does not move ~/.llama. cp -f /data/.e2e-rag-seed/kv_store.db /data/src/.llama/storage/rag/kv_store.db chmod -R 777 /data/src /data/.e2e-rag-seed echo "RAG data extracted successfully" @@ -85,6 +87,9 @@ spec: value: "/opt/app-root/src" - name: HOME value: "/opt/app-root/src" + # Prevent OGX from shutil.move(~/.llama → ~/.ogx) which would steal the fixture. + - name: OGX_CONFIG_DIR + value: "/opt/app-root/src/.ogx" - name: KV_STORE_PATH value: "/opt/app-root/src/.llama/storage/kv_store.db" - name: KV_RAG_PATH @@ -134,6 +139,8 @@ spec: RAG_SEED="/opt/app-root/src/.llama/storage/.e2e-rag-seed/kv_store.db" RAG_CM_GZ="/opt/app-root/rag-data-cm/kv_store.db.gz" RAG_WORK="${KV_RAG_PATH:-/opt/app-root/src/.llama/storage/rag/kv_store.db}" + export OGX_CONFIG_DIR="${OGX_CONFIG_DIR:-/opt/app-root/src/.ogx}" + mkdir -p "$OGX_CONFIG_DIR" "$(dirname "$RAG_WORK")" restore_rag_seed() { mkdir -p "$(dirname "$RAG_WORK")" if [[ -f "$RAG_CM_GZ" ]]; then diff --git a/tests/e2e-prow/rhoai/pipeline-konflux.sh b/tests/e2e-prow/rhoai/pipeline-konflux.sh index 90914906e..c1aa7158f 100755 --- a/tests/e2e-prow/rhoai/pipeline-konflux.sh +++ b/tests/e2e-prow/rhoai/pipeline-konflux.sh @@ -285,9 +285,11 @@ oc logs llama-stack-service -n "$NAMESPACE" 2>&1 | grep '\[e2e-rag\]' \ | while IFS= read -r line || [[ -n "$line" ]]; do echo "[e2e] $line"; done || true if ! oc exec llama-stack-service -n "$NAMESPACE" -- /bin/bash -c ' set -e - export RAG_WORK="${KV_RAG_PATH:-/opt/app-root/src/.llama/storage/rag/kv_store.db}" + export RAG_WORK="${KV_RAG_PATH:-/opt/app-root/.e2e-rag-work/kv_store.db}" echo "[e2e-rag] live KV_RAG_PATH=${KV_RAG_PATH:-}" + echo "[e2e-rag] live OGX_CONFIG_DIR=${OGX_CONFIG_DIR:-}" echo "[e2e-rag] live FAISS_VECTOR_STORE_ID=${FAISS_VECTOR_STORE_ID:-}" + # Fixture must still be at KV_RAG_PATH after ogx start (not moved by ~/.llama migration). python3 /opt/app-root/scripts/e2e_verify_rag_fixture.py if [[ -f /tmp/enriched-run.yaml ]]; then python3 /opt/app-root/scripts/e2e_verify_enriched_rag_config.py /tmp/enriched-run.yaml From 5a97e84ad718e7375687074c63d870860e5095f2 Mon Sep 17 00:00:00 2001 From: Radovan Fuchs Date: Tue, 11 Aug 2026 10:34:38 +0200 Subject: [PATCH 16/16] cleanup --- deploy/llama-stack/test.containerfile | 6 +- docker-compose.yaml | 2 - scripts/e2e_verify_enriched_rag_config.py | 229 ------------------ scripts/e2e_verify_rag_fixture.py | 140 ----------- scripts/llama-stack-entrypoint.sh | 7 - .../lightspeed/llama-stack-openai.yaml | 11 +- tests/e2e-prow/rhoai/pipeline-konflux.sh | 68 ------ .../test_e2e_verify_enriched_rag_config.py | 219 ----------------- 8 files changed, 3 insertions(+), 679 deletions(-) delete mode 100644 scripts/e2e_verify_enriched_rag_config.py delete mode 100644 scripts/e2e_verify_rag_fixture.py delete mode 100644 tests/unit/scripts/test_e2e_verify_enriched_rag_config.py diff --git a/deploy/llama-stack/test.containerfile b/deploy/llama-stack/test.containerfile index 93ea54aaa..92d4d649e 100644 --- a/deploy/llama-stack/test.containerfile +++ b/deploy/llama-stack/test.containerfile @@ -42,12 +42,8 @@ RUN mkdir -p /opt/app-root/src/.llama/storage \ # Copy enrichment scripts for runtime config enrichment COPY src/llama_stack_configuration.py /opt/app-root/llama_stack_configuration.py COPY scripts/llama-stack-entrypoint.sh /opt/app-root/enrich-entrypoint.sh -COPY scripts/e2e_verify_enriched_rag_config.py /opt/app-root/scripts/e2e_verify_enriched_rag_config.py -COPY scripts/e2e_verify_rag_fixture.py /opt/app-root/scripts/e2e_verify_rag_fixture.py RUN chmod +x /opt/app-root/enrich-entrypoint.sh && \ - chown -R 1001:0 /opt/app-root/enrich-entrypoint.sh \ - /opt/app-root/llama_stack_configuration.py \ - /opt/app-root/scripts + chown 1001:0 /opt/app-root/enrich-entrypoint.sh /opt/app-root/llama_stack_configuration.py # Switch back to the original user USER 1001 diff --git a/docker-compose.yaml b/docker-compose.yaml index 15e504676..f6097395e 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,8 +16,6 @@ services: # Host copies so `docker compose up` picks up script changes without rebuilding llama-stack - ./scripts/llama-stack-entrypoint.sh:/opt/app-root/enrich-entrypoint.sh:ro,z - ./src/llama_stack_configuration.py:/opt/app-root/llama_stack_configuration.py:ro,z - - ./scripts/e2e_verify_enriched_rag_config.py:/opt/app-root/scripts/e2e_verify_enriched_rag_config.py:ro,z - - ./scripts/e2e_verify_rag_fixture.py:/opt/app-root/scripts/e2e_verify_rag_fixture.py:ro,z - ${GCP_KEYS_PATH:-./tmp/.gcp-keys-dummy}:/opt/app-root/.gcp-keys:ro - ./lightspeed-stack.yaml:/opt/app-root/lightspeed-stack.yaml:ro,z - llama-storage:/opt/app-root/src/.llama/storage diff --git a/scripts/e2e_verify_enriched_rag_config.py b/scripts/e2e_verify_enriched_rag_config.py deleted file mode 100644 index 9aed01e47..000000000 --- a/scripts/e2e_verify_enriched_rag_config.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python3 -"""Fail-fast checks for OGX-enriched FAISS/BYOK config (Konflux/GH e2e). - -Validates that enrichment produced a usable FAISS setup for the e2e fixture: -namespaced persistence, resolvable SQLite db_path, and a vector_store_id that -expands to FAISS_VECTOR_STORE_ID (not a leftover ``${env....}`` literal). -""" - -from __future__ import annotations - -import os -import re -import sys -from pathlib import Path -from typing import Any - -import yaml - -# OGX uses ``${env.VAR}`` or ``${env.VAR:=default}`` (note ``:=``, not bare ``=``). -_ENV_PATTERN = re.compile( - r"\$\{env\.([A-Za-z_][A-Za-z0-9_]*)(?::=([^}]*))?\}" -) - - -def expand_env_refs(value: str) -> str: - """Expand ``${env.VAR}`` / ``${env.VAR:=default}`` like OGX ``replace_env_vars``. - - Parameters: - value: Raw config string that may contain env references. - - Returns: - String with all env references replaced from the process environment. - """ - - def _replace(match: re.Match[str]) -> str: - name = match.group(1) - default = match.group(2) - env_val = os.environ.get(name) - if env_val is not None and env_val != "": - return env_val - if default is not None: - return default - return "" - - return _ENV_PATTERN.sub(_replace, value) - - -def _byok_backends(backends: dict[str, Any]) -> dict[str, Any]: - """Return storage backends used for BYOK / RAG SQLite.""" - return { - name: backend - for name, backend in backends.items() - if "byok" in name or name in {"kv_rag", "kv_default"} - } - - -def _faiss_providers(vector_io: list[Any]) -> list[dict[str, Any]]: - """Return inline FAISS vector_io provider dicts.""" - out: list[dict[str, Any]] = [] - for provider in vector_io: - if not isinstance(provider, dict): - continue - ptype = str(provider.get("provider_type") or "") - if ptype == "inline::faiss" or str(provider.get("provider_id", "")).startswith( - "byok_" - ): - out.append(provider) - return out - - -def verify_enriched_config(cfg: dict[str, Any]) -> list[str]: - """Return human-readable errors for an enriched Llama/OGX run config. - - Parameters: - cfg: Parsed enriched ``run.yaml`` mapping. - - Returns: - List of error strings (empty means OK). Skips checks when no BYOK/FAISS - providers are present. - """ - errors: list[str] = [] - backends = (cfg.get("storage") or {}).get("backends") or {} - vector_io = ((cfg.get("providers") or {}).get("vector_io")) or [] - if not isinstance(vector_io, list): - vector_io = [] - stores = ((cfg.get("registered_resources") or {}).get("vector_stores")) or [] - if not isinstance(stores, list): - stores = [] - - faiss_providers = _faiss_providers(vector_io) - byok_backends = { - name: backend - for name, backend in backends.items() - if isinstance(name, str) and name.startswith("byok_") - } - - if not faiss_providers and not byok_backends: - print("[e2e-rag] enriched config: no BYOK/FAISS providers — skipping") - return [] - - expected_id = os.environ.get("FAISS_VECTOR_STORE_ID", "").strip() - kv_rag_path = os.environ.get("KV_RAG_PATH", "").strip() - - print(f"[e2e-rag] KV_RAG_PATH={kv_rag_path!r}") - print(f"[e2e-rag] KV_STORE_PATH={os.environ.get('KV_STORE_PATH')!r}") - print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected_id!r}") - - for name, backend in _byok_backends(backends).items(): - print(f"[e2e-rag] storage.backends[{name}]={backend}") - - for provider in faiss_providers: - print(f"[e2e-rag] vector_io provider={provider}") - persistence = (provider.get("config") or {}).get("persistence") or {} - namespace = persistence.get("namespace") - backend_name = persistence.get("backend") - if namespace != "vector_io::faiss": - errors.append( - f"provider {provider.get('provider_id')!r} persistence.namespace " - f"is {namespace!r}, expected 'vector_io::faiss'" - ) - if str(provider.get("provider_id", "")).startswith("byok_"): - if not backend_name or not str(backend_name).startswith("byok_"): - errors.append( - f"BYOK provider {provider.get('provider_id')!r} " - f"persistence.backend is {backend_name!r}" - ) - elif backend_name not in backends: - errors.append( - f"BYOK persistence.backend {backend_name!r} missing from " - "storage.backends" - ) - - if not byok_backends and any( - str(p.get("provider_id", "")).startswith("byok_") for p in faiss_providers - ): - errors.append("BYOK FAISS providers present but no byok_* storage backends") - - for name, backend in byok_backends.items(): - if not isinstance(backend, dict): - errors.append(f"storage.backends[{name}] is not a mapping") - continue - raw_path = str(backend.get("db_path") or "") - resolved = expand_env_refs(raw_path) - print(f"[e2e-rag] resolved db_path[{name}]={resolved!r} (raw={raw_path!r})") - if "${env." in resolved: - errors.append( - f"storage.backends[{name}].db_path still has unresolved env refs: " - f"{resolved!r}" - ) - continue - if not resolved: - errors.append(f"storage.backends[{name}].db_path resolved empty") - continue - # HOME-relative defaults from run-ci.yaml - path = Path(resolved).expanduser() - if not path.is_file(): - errors.append(f"storage.backends[{name}].db_path does not exist: {path}") - continue - size = path.stat().st_size - print(f"[e2e-rag] db_path[{name}] size={size}") - if size < 1_048_576: - errors.append( - f"storage.backends[{name}].db_path too small ({size} bytes): {path}" - ) - if kv_rag_path and path.resolve() != Path(kv_rag_path).expanduser().resolve(): - # Warn in logs; only error when FAISS id is set (e2e fixture path). - if expected_id: - errors.append( - f"storage.backends[{name}].db_path {path} != KV_RAG_PATH " - f"{kv_rag_path}" - ) - - if not stores: - errors.append("registered_resources.vector_stores is empty after enrichment") - for store in stores: - print(f"[e2e-rag] registered vector_store={store}") - if not isinstance(store, dict): - errors.append(f"vector_store entry is not a mapping: {store!r}") - continue - raw_id = str(store.get("vector_store_id") or "") - resolved_id = expand_env_refs(raw_id) - print( - f"[e2e-rag] resolved vector_store_id={resolved_id!r} (raw={raw_id!r})" - ) - if "${env." in resolved_id or not resolved_id: - errors.append( - f"vector_store_id did not expand (raw={raw_id!r}, " - f"resolved={resolved_id!r}); is FAISS_VECTOR_STORE_ID set?" - ) - elif expected_id and resolved_id != expected_id: - errors.append( - f"vector_store_id {resolved_id!r} != FAISS_VECTOR_STORE_ID " - f"{expected_id!r}" - ) - - if expected_id and not expected_id.startswith("vs_"): - errors.append( - f"FAISS_VECTOR_STORE_ID looks invalid for OGX fixture: {expected_id!r}" - ) - - return errors - - -def main(argv: list[str] | None = None) -> int: - """CLI entry: verify enriched run.yaml path (default ``/tmp/enriched-run.yaml``).""" - args = list(sys.argv[1:] if argv is None else argv) - path = args[0] if args else "/tmp/enriched-run.yaml" - if not os.path.isfile(path): - print(f"FATAL: enriched config missing: {path}", file=sys.stderr) - return 1 - - with open(path, encoding="utf-8") as fh: - cfg = yaml.safe_load(fh) or {} - if not isinstance(cfg, dict): - print(f"FATAL: enriched config is not a mapping: {path}", file=sys.stderr) - return 1 - - print(f"[e2e-rag] verifying enriched config={path}") - errors = verify_enriched_config(cfg) - if errors: - for err in errors: - print(f"FATAL: {err}", file=sys.stderr) - return 1 - print("[e2e-rag] enriched FAISS/BYOK config OK") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/e2e_verify_rag_fixture.py b/scripts/e2e_verify_rag_fixture.py deleted file mode 100644 index adaab77b1..000000000 --- a/scripts/e2e_verify_rag_fixture.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -"""Verify the e2e FAISS RAG SQLite fixture before starting Llama Stack. - -Prints [e2e-rag] lines for Konflux/Prow log dumps and exits non-zero when the -fixture is missing the FAISS index, does not match FAISS_VECTOR_STORE_ID, or -cannot be deserialized to a non-empty FAISS index. -""" - -from __future__ import annotations - -import base64 -import io -import json -import os -import sqlite3 -import sys - - -def _check_deserialized_ntotal(index_blob: str) -> int: - """Deserialize the FAISS index payload and return ``ntotal``. - - Parameters: - index_blob: JSON string stored under the faiss_index KV key. - - Returns: - Number of vectors in the deserialized index. - - Raises: - Exception: If faiss/numpy are unavailable or deserialization fails. - """ - import faiss # pylint: disable=import-outside-toplevel - import numpy as np # pylint: disable=import-outside-toplevel - - data = json.loads(index_blob) - chunks = data.get("chunk_by_index") or {} - buffer = io.BytesIO(base64.b64decode(data["faiss_index"])) - index = faiss.deserialize_index(np.load(buffer, allow_pickle=False)) - print(f"[e2e-rag] chunk_by_index={len(chunks)} ntotal={index.ntotal} dim={index.d}") - return int(index.ntotal) - - -def main() -> int: - """Validate RAG fixture path from env and return a process exit code.""" - path = os.environ.get("RAG_WORK") or os.environ.get("KV_RAG_PATH") - expected = os.environ.get("FAISS_VECTOR_STORE_ID", "") - if not path: - print("FATAL: RAG_WORK or KV_RAG_PATH must be set", file=sys.stderr) - return 1 - if not os.path.isfile(path): - print(f"FATAL: RAG fixture missing: {path}", file=sys.stderr) - return 1 - - size = os.path.getsize(path) - conn = sqlite3.connect(path) - rows = conn.execute( - "SELECT key, length(value), value FROM kvstore WHERE key LIKE '%faiss_index%'" - ).fetchall() - vs_keys = conn.execute( - "SELECT key FROM kvstore WHERE key LIKE '%vector_stores:v%::%' " - "AND key NOT LIKE '%openai%' AND key NOT LIKE '%files%'" - ).fetchall() - conn.close() - - namespaced_index = [ - (key, val_len, blob) - for key, val_len, blob in rows - if key.startswith("vector_io::faiss:faiss_index:") - ] - namespaced_vs = [ - key for (key,) in vs_keys if key.startswith("vector_io::faiss:vector_stores:") - ] - - print(f"[e2e-rag] fixture={path} size={size}") - print(f"[e2e-rag] FAISS_VECTOR_STORE_ID={expected!r}") - print(f"[e2e-rag] vector_stores keys={vs_keys}") - print( - f"[e2e-rag] faiss_index keys=" - f"{[(key, val_len) for key, val_len, _ in rows]}" - ) - print(f"[e2e-rag] namespaced vector_stores={namespaced_vs}") - print( - f"[e2e-rag] namespaced faiss_index=" - f"{[(key, val_len) for key, val_len, _ in namespaced_index]}" - ) - - if size < 1_048_576: - print(f"FATAL: RAG fixture too small: {size}", file=sys.stderr) - return 1 - if not rows: - print("FATAL: no faiss_index key in RAG fixture", file=sys.stderr) - return 1 - if not namespaced_index: - print( - "FATAL: no OGX 1.0 namespaced faiss_index key " - "(expected prefix vector_io::faiss:faiss_index:)", - file=sys.stderr, - ) - return 1 - if not namespaced_vs: - print( - "FATAL: no OGX 1.0 namespaced vector_stores key " - "(expected prefix vector_io::faiss:vector_stores:)", - file=sys.stderr, - ) - return 1 - - key, val_len, index_blob = namespaced_index[0] - if expected: - matched = [ - (k, length, blob) - for k, length, blob in namespaced_index - if expected in k - ] - if not matched: - print( - f"FATAL: FAISS_VECTOR_STORE_ID {expected!r} not in any " - f"namespaced index key {[k for k, _, _ in namespaced_index]!r}", - file=sys.stderr, - ) - return 1 - key, val_len, index_blob = matched[0] - if val_len < 100_000: - print(f"FATAL: faiss_index value too small: {val_len}", file=sys.stderr) - return 1 - - try: - ntotal = _check_deserialized_ntotal(index_blob) - except Exception as exc: # pylint: disable=broad-exception-caught - print(f"FATAL: failed to deserialize FAISS index: {exc}", file=sys.stderr) - return 1 - if ntotal < 1: - print("FATAL: deserialized FAISS index is empty (ntotal=0)", file=sys.stderr) - return 1 - - print("[e2e-rag] FAISS fixture OK") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/llama-stack-entrypoint.sh b/scripts/llama-stack-entrypoint.sh index 3af5c6714..2ddcfd2e8 100755 --- a/scripts/llama-stack-entrypoint.sh +++ b/scripts/llama-stack-entrypoint.sh @@ -7,7 +7,6 @@ set -e INPUT_CONFIG="${LLAMA_STACK_CONFIG:-/opt/app-root/run.yaml}" ENRICHED_CONFIG="/tmp/enriched-run.yaml" LIGHTSPEED_CONFIG="${LIGHTSPEED_CONFIG:-/opt/app-root/lightspeed-stack.yaml}" -VERIFY_ENRICHED="${E2E_VERIFY_ENRICHED_RAG_CONFIG:-/opt/app-root/scripts/e2e_verify_enriched_rag_config.py}" # Enrich config if lightspeed config exists if [ -f "$LIGHTSPEED_CONFIG" ]; then @@ -20,12 +19,6 @@ if [ -f "$LIGHTSPEED_CONFIG" ]; then if [ -f "$ENRICHED_CONFIG" ] && [ "$ENRICHMENT_FAILED" -eq 0 ]; then echo "Using enriched config: $ENRICHED_CONFIG" - # Fail fast when BYOK/FAISS enrichment is wrong for OGX 1.0 (namespace / env / path). - if [ -f "$VERIFY_ENRICHED" ]; then - /opt/app-root/.venv/bin/python3 "$VERIFY_ENRICHED" "$ENRICHED_CONFIG" - else - echo "[e2e-rag] WARNING: missing $VERIFY_ENRICHED — skipping enriched config check" - fi exec ogx stack run "$ENRICHED_CONFIG" fi fi diff --git a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml index da5f18004..292db1f0b 100644 --- a/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml +++ b/tests/e2e-prow/rhoai/manifests/lightspeed/llama-stack-openai.yaml @@ -3,13 +3,8 @@ # Needs ConfigMaps: llama-stack-config (run.yaml), rag-data (kv_store.db.gz), lightspeed-stack-config; # optional llama-stack-source for repo_url / repo_revision. # -# RAG: seeded in setup-from-source from rag-data ConfigMap (gzip); main re-inflates, verifies the -# OGX 1.0 namespaced FAISS index at KV_RAG_PATH, then enrich-entrypoint checks BYOK backends -# (persistence.namespace + resolved db_path / vector_store_id) before `ogx stack run`. -# -# KV_RAG_PATH must NOT live under ~/.llama: OGX migrate_legacy_config_dir() moves ~/.llama → ~/.ogx -# on startup when OGX_CONFIG_DIR is unset, which orphans the fixture path; SQLite then recreates an -# empty 12KiB DB and register_resources writes only vector_stores metadata (0 chunks). +# RAG fixture lives at KV_RAG_PATH outside ~/.llama, with OGX_CONFIG_DIR set, so +# migrate_legacy_config_dir() cannot move the fixture away on startup. apiVersion: v1 kind: Pod metadata: @@ -216,8 +211,6 @@ spec: } # Re-inflate golden FAISS fixture (same bytes GH bind-mounts from tests/e2e/rag). restore_rag_seed - # Fail fast with evidence in pod logs (e2e-ops dumps these on scenario failure). - RAG_WORK="$RAG_WORK" python3 /opt/app-root/scripts/e2e_verify_rag_fixture.py if [[ ! -x /opt/app-root/enrich-entrypoint.sh ]]; then echo "FATAL: missing /opt/app-root/enrich-entrypoint.sh (init should install it)" exit 1 diff --git a/tests/e2e-prow/rhoai/pipeline-konflux.sh b/tests/e2e-prow/rhoai/pipeline-konflux.sh index c1aa7158f..9b90dfd4d 100755 --- a/tests/e2e-prow/rhoai/pipeline-konflux.sh +++ b/tests/e2e-prow/rhoai/pipeline-konflux.sh @@ -278,32 +278,6 @@ if ! oc wait pod/lightspeed-stack-service pod/llama-stack-service \ fi log "✅ Both service pods are ready" -# OGX 1.0 / BYOK evidence: fail before port-forward if fixture or enriched config is wrong. -progress "Verifying live RAG fixture + enriched FAISS config in llama-stack pod" -echo "[e2e] ========== llama-stack [e2e-rag] startup lines ==========" -oc logs llama-stack-service -n "$NAMESPACE" 2>&1 | grep '\[e2e-rag\]' \ - | while IFS= read -r line || [[ -n "$line" ]]; do echo "[e2e] $line"; done || true -if ! oc exec llama-stack-service -n "$NAMESPACE" -- /bin/bash -c ' - set -e - export RAG_WORK="${KV_RAG_PATH:-/opt/app-root/.e2e-rag-work/kv_store.db}" - echo "[e2e-rag] live KV_RAG_PATH=${KV_RAG_PATH:-}" - echo "[e2e-rag] live OGX_CONFIG_DIR=${OGX_CONFIG_DIR:-}" - echo "[e2e-rag] live FAISS_VECTOR_STORE_ID=${FAISS_VECTOR_STORE_ID:-}" - # Fixture must still be at KV_RAG_PATH after ogx start (not moved by ~/.llama migration). - python3 /opt/app-root/scripts/e2e_verify_rag_fixture.py - if [[ -f /tmp/enriched-run.yaml ]]; then - python3 /opt/app-root/scripts/e2e_verify_enriched_rag_config.py /tmp/enriched-run.yaml - else - echo "FATAL: /tmp/enriched-run.yaml missing after llama-stack start" >&2 - exit 1 - fi -'; then - echo "❌ Live RAG / enriched config verification failed" | tee /dev/stderr - e2e_echo_pod_logs 250 - exit 1 -fi -log "✅ Live RAG fixture + enriched FAISS/BYOK config OK" - if [ "$QUIET" = "1" ]; then e2e_echo_pod_logs 80 else @@ -441,48 +415,6 @@ log "LCS accessible at: http://$E2E_LSC_HOSTNAME:8080" log "Mock JWKS accessible at: http://$E2E_JWKS_HOSTNAME:8000" log "Llama Stack (e2e client hooks) at: http://$E2E_LLAMA_HOSTNAME:$E2E_LLAMA_PORT" -# Fail fast if FAISS is registered but returns zero chunks (matches prior Konflux RAG mode). -if [[ -n "${FAISS_VECTOR_STORE_ID:-}" ]]; then - progress "Smoke-testing FAISS vector_io.query for $FAISS_VECTOR_STORE_ID" - RAG_SMOKE_OUT="$(mktemp)" - if ! curl -sf -X POST "http://localhost:8321/v1/vector-io/query" \ - -H "Content-Type: application/json" \ - -d "{\"vector_store_id\":\"${FAISS_VECTOR_STORE_ID}\",\"query\":\"What is the title of the article from Paul?\",\"params\":{\"max_chunks\":5,\"mode\":\"vector\"}}" \ - >"$RAG_SMOKE_OUT"; then - echo "❌ FAISS smoke query HTTP failed" | tee /dev/stderr - e2e_echo_pod_logs 250 - rm -f "$RAG_SMOKE_OUT" - exit 1 - fi - RAG_SMOKE_CHUNKS="$(python3 -c " -import json,sys -try: - data=json.load(open(sys.argv[1], encoding='utf-8')) -except Exception as e: - print(f'parse-error:{e}') - sys.exit(0) -chunks=data.get('chunks') or data.get('data') or [] -if isinstance(data, dict) and not chunks: - # QueryChunksResponse shapes vary; count any list-like payload values. - for v in data.values(): - if isinstance(v, list): - chunks=v - break -print(len(chunks) if isinstance(chunks, list) else 0) -" "$RAG_SMOKE_OUT" 2>/dev/null || echo 0)" - log "[e2e-rag] smoke query chunks=${RAG_SMOKE_CHUNKS} body=$(head -c 400 "$RAG_SMOKE_OUT" | tr '\n' ' ')" - if [[ "${RAG_SMOKE_CHUNKS}" =~ ^[0-9]+$ ]] && [[ "${RAG_SMOKE_CHUNKS}" -lt 1 ]]; then - echo "❌ FAISS smoke query returned 0 chunks — RAG e2e would fail" | tee /dev/stderr - e2e_echo_pod_logs 250 - rm -f "$RAG_SMOKE_OUT" - exit 1 - fi - rm -f "$RAG_SMOKE_OUT" - log "✅ FAISS smoke query returned ${RAG_SMOKE_CHUNKS} chunk(s)" -else - log "⚠️ FAISS_VECTOR_STORE_ID unset — skipping RAG smoke query" -fi - #======================================== # 7. RUN TESTS #======================================== diff --git a/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py b/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py deleted file mode 100644 index 75d325b05..000000000 --- a/tests/unit/scripts/test_e2e_verify_enriched_rag_config.py +++ /dev/null @@ -1,219 +0,0 @@ -"""Unit tests for scripts/e2e_verify_enriched_rag_config.py.""" - -from __future__ import annotations - -import importlib.util -import sys -from pathlib import Path - -import pytest - -SCRIPT = ( - Path(__file__).resolve().parents[3] - / "scripts" - / "e2e_verify_enriched_rag_config.py" -) - - -def _load_module(): - """Load the verify script as a module without installing the package.""" - spec = importlib.util.spec_from_file_location( - "e2e_verify_enriched_rag_config", SCRIPT - ) - assert spec is not None and spec.loader is not None - module = importlib.util.module_from_spec(spec) - sys.modules[spec.name] = module - spec.loader.exec_module(module) - return module - - -@pytest.fixture(name="verify_mod") -def verify_mod_fixture(): - """Import the enriched-config verifier script.""" - return _load_module() - - -def test_expand_env_refs_uses_env_and_default(verify_mod, monkeypatch): - """Env expansion matches OGX ``${env.VAR:=default}`` behavior.""" - monkeypatch.setenv("KV_RAG_PATH", "/tmp/fixture.db") - monkeypatch.delenv("MISSING_VAR", raising=False) - assert ( - verify_mod.expand_env_refs("${env.KV_RAG_PATH:=/unused}") == "/tmp/fixture.db" - ) - assert verify_mod.expand_env_refs("${env.MISSING_VAR:=fallback}") == "fallback" - - -def test_verify_enriched_config_ok(verify_mod, tmp_path, monkeypatch): - """Happy path: namespaced BYOK FAISS + existing fixture DB.""" - db = tmp_path / "kv_store.db" - db.write_bytes(b"x" * 1_100_000) - monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") - monkeypatch.setenv("KV_RAG_PATH", str(db)) - - cfg = { - "storage": { - "backends": { - "kv_rag": { - "type": "kv_sqlite", - "db_path": "${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db}", - }, - "byok_e2e-test-docs_storage": { - "type": "kv_sqlite", - "db_path": "${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db}", - }, - } - }, - "providers": { - "vector_io": [ - { - "provider_id": "faiss", - "provider_type": "inline::faiss", - "config": { - "persistence": { - "namespace": "vector_io::faiss", - "backend": "kv_rag", - } - }, - }, - { - "provider_id": "byok_e2e-test-docs", - "provider_type": "inline::faiss", - "config": { - "persistence": { - "namespace": "vector_io::faiss", - "backend": "byok_e2e-test-docs_storage", - } - }, - }, - ] - }, - "registered_resources": { - "vector_stores": [ - { - "vector_store_id": "${env.FAISS_VECTOR_STORE_ID}", - "provider_id": "byok_e2e-test-docs", - "embedding_model": "sentence-transformers/all-mpnet-base-v2", - "embedding_dimension": 768, - } - ] - }, - } - - assert verify_mod.verify_enriched_config(cfg) == [] - - -def test_verify_enriched_config_rejects_wrong_namespace( - verify_mod, tmp_path, monkeypatch -): - """Missing OGX persistence.namespace must fail.""" - db = tmp_path / "kv_store.db" - db.write_bytes(b"x" * 1_100_000) - monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") - monkeypatch.setenv("KV_RAG_PATH", str(db)) - - cfg = { - "storage": { - "backends": { - "byok_e2e-test-docs_storage": { - "type": "kv_sqlite", - "db_path": str(db), - } - } - }, - "providers": { - "vector_io": [ - { - "provider_id": "byok_e2e-test-docs", - "provider_type": "inline::faiss", - "config": { - "persistence": { - "namespace": "wrong", - "backend": "byok_e2e-test-docs_storage", - } - }, - } - ] - }, - "registered_resources": { - "vector_stores": [{"vector_store_id": "vs_abc"}] - }, - } - - errors = verify_mod.verify_enriched_config(cfg) - assert any("persistence.namespace" in err for err in errors) - - -def test_verify_enriched_config_rejects_unexpanded_store_id( - verify_mod, tmp_path, monkeypatch -): - """Unset FAISS_VECTOR_STORE_ID leaves a literal env ref — must fail.""" - db = tmp_path / "kv_store.db" - db.write_bytes(b"x" * 1_100_000) - monkeypatch.delenv("FAISS_VECTOR_STORE_ID", raising=False) - monkeypatch.setenv("KV_RAG_PATH", str(db)) - - cfg = { - "storage": { - "backends": { - "byok_e2e-test-docs_storage": { - "type": "kv_sqlite", - "db_path": str(db), - } - } - }, - "providers": { - "vector_io": [ - { - "provider_id": "byok_e2e-test-docs", - "provider_type": "inline::faiss", - "config": { - "persistence": { - "namespace": "vector_io::faiss", - "backend": "byok_e2e-test-docs_storage", - } - }, - } - ] - }, - "registered_resources": { - "vector_stores": [ - {"vector_store_id": "${env.FAISS_VECTOR_STORE_ID}"} - ] - }, - } - - errors = verify_mod.verify_enriched_config(cfg) - assert any("did not expand" in err for err in errors) - - -def test_main_ok_with_temp_yaml(verify_mod, tmp_path, monkeypatch): - """CLI main returns 0 for a valid enriched YAML file.""" - db = tmp_path / "kv_store.db" - db.write_bytes(b"x" * 1_100_000) - monkeypatch.setenv("FAISS_VECTOR_STORE_ID", "vs_abc") - monkeypatch.setenv("KV_RAG_PATH", str(db)) - - cfg_path = tmp_path / "enriched-run.yaml" - cfg_path.write_text( - f""" -storage: - backends: - byok_e2e-test-docs_storage: - type: kv_sqlite - db_path: {db} -providers: - vector_io: - - provider_id: byok_e2e-test-docs - provider_type: inline::faiss - config: - persistence: - namespace: vector_io::faiss - backend: byok_e2e-test-docs_storage -registered_resources: - vector_stores: - - vector_store_id: vs_abc -""", - encoding="utf-8", - ) - - assert verify_mod.main([str(cfg_path)]) == 0