From e58f6c092edef97737b9c034f5f4fde0293a3b0d Mon Sep 17 00:00:00 2001 From: mptyl Date: Sat, 27 Jun 2026 08:20:36 +0200 Subject: [PATCH] fix(harness): workspace + write-URL + L2 tests per accesso REST reale Bug trovato provando la connessione reale col .env: il write endpoint vive su un PATH DEDICATO /vector/write/v1/ (non /vector/v1/), e il modello Config ha write_rest/ vector_write_rest a TOP-LEVEL (non nidificati in vector_db). - .env.example: aggiunge THOTH_VEC_WRITE_REST_URL (path dedicato del writer, con avviso che le due chiavi valgono su path separati). - workspaces/chirone-test.yaml: riscritto allineato a chirone.example.yaml + config.py (vector_rest/vector_write_rest top-level; write_rest punta a THOTH_VEC_WRITE_REST_URL). - tests/l2/*: corretti gli accessi strutturali (ws.vector_write_rest invece di ws.vector_db.write_rest; ws.vector_rest invece di ws.vector_db.rest). test_value_grounding_real skip-when-import-fails su nsp.lshindex (modulo deferred da B3). Verificato end-to-end: save_one_memory (embeddings -> writer REST /vector/write/v1/ -> upsert pgvector -> read-back reader) PASSED. Suite L0+L1: 109 passed. Suite L2: 4 passed, 1 skipped (lshindex deferred). Nota operativa: THOTH_SSL_CA va lasciato VUOTO sulla workstation (cert GoDaddy pubblico in certifi). I campi direct-transport (THOTH_DB_*, THOTH_VEC_PASSWORD) sono obbligatori per il modello ma inutilizzati in transport=rest: riempiti con dummy nel .env locale (come faceva ChironeWp3). --- harness/.env.example | 3 + harness/tests/l2/test_memory_save_one_real.py | 10 +-- harness/tests/l2/test_session_ablazione.py | 5 +- harness/tests/l2/test_value_grounding_real.py | 5 +- harness/workspaces/chirone-test.yaml | 68 ++++++++++++------- 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/harness/.env.example b/harness/.env.example index 38f25a96..74d49a13 100644 --- a/harness/.env.example +++ b/harness/.env.example @@ -20,6 +20,9 @@ THOTH_VEC_REST_URL=https://host/vector/v1/ THOTH_VEC_API_KEY= # Vector REST — SCRITTURA controllata (upsert only) da client remoti autorizzati +# Path DEDICATO: /vector/write/v1/ (NON /vector/v1/). Le due chiavi valgono su path +# separati: la writer key su /vector/v1/ -> 401, e viceversa. Solo postazioni che indicizzano. +THOTH_VEC_WRITE_REST_URL=https://host/vector/write/v1/ THOTH_VEC_WRITE_API_KEY= # Vector direct loading (server-only) diff --git a/harness/tests/l2/test_memory_save_one_real.py b/harness/tests/l2/test_memory_save_one_real.py index 1677b7ab..34d66c31 100644 --- a/harness/tests/l2/test_memory_save_one_real.py +++ b/harness/tests/l2/test_memory_save_one_real.py @@ -27,10 +27,10 @@ def test_save_one_upserts_to_real_pgvector(l2_env): from nsp.vectorstore.rest_client import VectorRestClient ws = load_workspace(WORKSPACE) - if not ws.vector_db.write_rest or not ws.vector_db.write_rest.api_key.strip(): - pytest.skip("vector_db.write_rest not configured (no writer key)") + if not ws.vector_write_rest or not ws.vector_write_rest.api_key.strip(): + pytest.skip("vector_write_rest not configured (no writer key)") - writer = VectorRestClient(ws.vector_db.write_rest) + writer = VectorRestClient(ws.vector_write_rest) embedder = OllamaEmbeddings(ws.embeddings) record = MemoryRecord( @@ -42,8 +42,8 @@ def test_save_one_upserts_to_real_pgvector(l2_env): upserted = save_one_memory([record], decision_seq=999, writer=writer, embedder=embedder) assert upserted >= 0 # idempotent: 0 on unchanged, >=1 on new/updated - # read it back via the READER key - reader = VectorRestClient(ws.vector_db.rest) + # read it back via the READER key (vector_rest, path /vector/v1/) + reader = VectorRestClient(ws.vector_rest) qvec = embedder.embed_query("ablazione") hits = reader.search_similar("memory", qvec, 10) ids = {h.get("metadata", {}).get("record_key", "") for h in hits} diff --git a/harness/tests/l2/test_session_ablazione.py b/harness/tests/l2/test_session_ablazione.py index f8476197..541e84a4 100644 --- a/harness/tests/l2/test_session_ablazione.py +++ b/harness/tests/l2/test_session_ablazione.py @@ -40,8 +40,9 @@ def test_workspace_chirone_test_loads(l2_env): ws = load_workspace(WORKSPACE) # secrets must be expanded (not the literal ${...} token) - assert not ws.vector_db.rest.api_key.startswith("${") - assert not ws.vector_db.write_rest.api_key.startswith("${") + assert not ws.rest.api_key.startswith("${") + assert not ws.vector_rest.api_key.startswith("${") + assert not ws.vector_write_rest.api_key.startswith("${") def test_pi_binary_available(l2_env): diff --git a/harness/tests/l2/test_value_grounding_real.py b/harness/tests/l2/test_value_grounding_real.py index a7ac07fd..c941c081 100644 --- a/harness/tests/l2/test_value_grounding_real.py +++ b/harness/tests/l2/test_value_grounding_real.py @@ -21,7 +21,10 @@ def test_ablazione_returns_multiple_columns(l2_env): a flag and a free-text patologia field) -- the whole point of D14a's non-collapsing aggregation. Requires a built LSH index (nsp lsh build).""" from nsp.config import LshConfig - from nsp.lshindex import load_index, query_index # ported with the lsh build path + try: + from nsp.lshindex import load_index, query_index # ported with the lsh build path + except ModuleNotFoundError: + pytest.skip("nsp.lshindex not yet ported (deferred from B3; lands with nsp lsh build)") from nsp.search import aggregate_lsh_multi # NOTE: this test assumes the LSH index was built (nsp lsh build --workspace diff --git a/harness/workspaces/chirone-test.yaml b/harness/workspaces/chirone-test.yaml index c8ba9a2a..1a5687ea 100644 --- a/harness/workspaces/chirone-test.yaml +++ b/harness/workspaces/chirone-test.yaml @@ -1,31 +1,20 @@ -# Workspace ThothII -- Chirone (DWH remoto) per i test L2. -# I segreti vivono SOLO in .env (${THOTH_*}). Endpoint + ruolo dai parametri di -# connessione L2 (spec Testing Strategy): DWH read-only via PostgREST, pgvector -# con doppia key (reader/writer) sullo stesso endpoint, TLS self-signed (CA = leaf). - -name: chirone-test -description: "Chirone DWH -- L2 test workspace" +# Workspace ThothII — Chirone (DWH remoto) per i test L2. +# Struttura allineata a workspaces/chirone.example.yaml e nsp/config.py. +# I segreti vivono SOLO in .env (${THOTH_*}). database: - database: postgres + host: ${THOTH_DB_HOST} + port: ${THOTH_DB_PORT} + database: ${THOTH_DB_NAME} schema: datawarehouse + user: ${THOTH_DB_USER} + password: ${THOTH_DB_PASSWORD} transport: rest - rest: - base_url: ${THOTH_DWH_REST_URL} - api_key: ${THOTH_DWH_API_KEY} - ssl_ca: ${THOTH_SSL_CA} -vector_db: - collection: chirone_docs - dim: 768 - rest: - base_url: ${THOTH_VEC_REST_URL} - api_key: ${THOTH_VEC_API_KEY} - ssl_ca: ${THOTH_SSL_CA} - write_rest: - base_url: ${THOTH_VEC_REST_URL} - api_key: ${THOTH_VEC_WRITE_API_KEY} - ssl_ca: ${THOTH_SSL_CA} +rest: + base_url: ${THOTH_DWH_REST_URL} + api_key: ${THOTH_DWH_API_KEY} + ssl_ca: ${THOTH_SSL_CA} paths: artifacts: artifacts @@ -33,12 +22,43 @@ paths: sessions: sessions embeddings: - provider: ollama base_url: ${THOTH_OLLAMA_URL} model: nomic-embed-text-v2-moe dim: 768 batch_size: 64 +# LOADING diretto del pgvector (server-only). Su workstation la lettura passa da +# vector_rest; i campi direct-transport non sono usati ma il modello DatabaseConfig +# li richiede, per cui si appoggiano alle stesse variabili del server (popolate nel +# .env del server; sul workstation restano dummy/inediti finche' transport=rest). +vector_db: + host: ${THOTH_VEC_HOST} + port: ${THOTH_VEC_PORT} + database: postgres + schema: vectors + user: ${THOTH_VEC_USER} + password: ${THOTH_VEC_PASSWORD} + +# LETTURA del pgvector via REST remota (rpc search_similar), ruolo vector_reader. +vector_rest: + base_url: ${THOTH_VEC_REST_URL} + api_key: ${THOTH_VEC_API_KEY} + ssl_ca: ${THOTH_SSL_CA} + +# SCRITTURA controllata del pgvector via REST remota (upsert/hash via RPC allowlist), +# ruolo vector_writer. Path DEDICATO /vector/write/v1/ (diverso dal reader): la chiave +# writer vale solo qui, la reader solo su /vector/v1/. Key SEPARATA dalla lettura. +vector_write_rest: + base_url: ${THOTH_VEC_WRITE_REST_URL} + api_key: ${THOTH_VEC_WRITE_API_KEY} + ssl_ca: ${THOTH_SSL_CA} + +vector: + max_chunk_chars: 4000 + +search: + rrf_k: 60 + execution: allow: [cte_test, explain, preview, aggregate, export] max_preview_rows: 10