Skip to content

week_3: Module C (The Librarian) — C.1 candidate retriever (in-memory + pgvector) + pipeline switch#937

Merged
northdpole merged 6 commits into
OWASP:mainfrom
PRAteek-singHWY:gsocmodule_C_week_3
Jul 9, 2026
Merged

week_3: Module C (The Librarian) — C.1 candidate retriever (in-memory + pgvector) + pipeline switch#937
northdpole merged 6 commits into
OWASP:mainfrom
PRAteek-singHWY:gsocmodule_C_week_3

Conversation

@PRAteek-singHWY

@PRAteek-singHWY PRAteek-singHWY commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

week_3: Module C (The Librarian) — C.1 semantic candidate retriever + pipeline switch

Stacked on #925 (Week 2). This branch is based on gsocmodule_C_week_2; only the top commit is new. Until #922 and #925 merge, the diff shows the W1+W2 commits too — I will rebase onto main as they land, shrinking it to the Week 3 files only.

Overview

Week 1 built the judge (golden dataset + scorer + harness). Week 2 built the parts that must never guess — the C.0 input boundary and the deterministic explicit-CRE fast path. This is the first part that does guess.

The problem: the explicit fast path only fires when a CRE id (ddd-ddd) is literally written in the chunk. Most chunks just describe a security concept in plain English, so for those C must find which of OpenCRE's CRE nodes the text is about. That is a search problem — and the first place in the pipeline that can be wrong.

This PR's role: build the search step (C.1). Embed the section text, cosine-match it against the CRE-node vector hub, and return the top-K (default 20) rank-ordered candidates — the shortlist the W4 cross-encoder reranks and later weeks turn into a confident yes/no. Because Week 1 already built the judge, this first ML component gets a measured score from day one (recall@k, see Results).

Scope: 1 new module + benchmark + 1 new test, plus config/CLI/harness wiring. No frontend, no migration, no behaviour change to OpenCRE proper. (The pgvector schema migration lands in W8 — see "not here".)

What changed

Area Files Description
C.1 retriever candidate_retriever.py (new) Two interchangeable backends behind one retrieve() seam, both emitting the same RFC RetrievalAudit (candidates[] rank-ordered with score_vector; reranked[] empty until W4). CandidateRetriever — in-memory sklearn cosine over an injected embed_fn + immutable CandidatePool. PgVectorRetriever — pushes the cosine into Postgres via the <=> operator over an embedding_vec column (scored 1 - distance), never loading the hub into RAM. build_retriever(backend, ...) is the factory. DI keeps the module import-light and hermetically testable (same pattern as W2's resolver). Reuses OpenCRE's embedding stack (PromptHandler.get_text_embeddings, db.get_embeddings_by_doc_type, cosine_similarity).
Backend select candidate_retriever.py, config_loader.py RetrieverBackend enum + CRE_LIBRARIAN_RETRIEVER_BACKEND. The RFC is silent on retrieval tech (it mandates only the audit trail), so the choice is ours. in_memory is the only backend that runs on SQLite, so CI/dev/harness use it; pgvector is the proposal's primary for prod Postgres.
Dim gate candidate_retriever.py CandidatePool.from_mapping rejects an empty or ragged hub; retrieve asserts query-vector width == stored CRE-vector width. A silent dim mismatch makes every cosine score meaningless → fails loudly (DimensionMismatchError). Postgres enforces this structurally via the fixed-width vector(dim) column.
Config config_loader.py, .env.example CRE_LIBRARIAN_RETRIEVER_BACKEND (validated), plus a documented Librarian env block.
CLI switch cre.py, cre_main.py --run_librarian / --librarian_dry_run (+ --librarian_source): read knowledge-queue sections → explicit resolver (C.0.5) → build_retriever-selected backend (C.1) → log the shortlist per section. Writes land W8, so this is dry-run only; --run_librarian without writes warns and behaves identically.
Benchmark benchmark_retriever.py (new) Times in-memory vs pgvector over the real CRE hub and checks top-1 agreement. in-memory always runs; the pgvector leg auto-skips (loudly, never silently passes) unless the DB is Postgres with the embedding_vec column present.
Eval harness evaluate_librarian.py --use_live_embeddings measures retrieval recall@k over the positive slice against the real DB + embedding model. Offline (CI default) it stays honest: the retriever is wired, but recall@k needs live embeddings — there is no offline number because the pool must be the real CRE vectors, and seeding it from golden text is exactly the leakage the hub firewall strips.
Tests candidate_retriever_test.py (new) 16 hermetic tests: cosine rank-ordering, top-K truncation + cap, the dim gate (ragged/zero-width/query mismatch), empty-pool rejection, score_vector/cre_name population, W3 audit shape; pgvector parsing + SQL-binding via a fake recording connection (no DB needed); and the build_retriever factory routing + arg-mismatch guards.

How the pieces connect

flowchart TB
    sec["Section (from C.0, W2)"]
    res["explicit_link_resolver (C.0.5, W2)<br/>regex ddd-ddd, no ML"]

    subgraph C1["C.1 — semantic retriever (this PR)"]
        embed["embed_fn(text)<br/>(PromptHandler.get_text_embeddings)"]
        factory["build_retriever(backend)"]
        inmem["CandidateRetriever<br/>sklearn cosine + top-K<br/>(SQLite / CI / harness)"]
        pg["PgVectorRetriever<br/>embedding_vec &lt;=&gt; query<br/>(Postgres prod)"]
        audit["RetrievalAudit<br/>candidates[] (score_vector)<br/>reranked[] = [] (W4)"]
    end

    rev["unknown / conflicting -> review (W2)"]
    rerank["CrossEncoderRanker (C.2, W4)<br/>reranks shortlist -> top 5"]

    sec --> res
    res -- "resolved" --> done["deterministic link"]
    res -- "no_reference" --> embed
    res -- "unknown / conflicting" --> rev
    embed --> factory
    factory --> inmem
    factory --> pg
    inmem --> audit
    pg --> audit
    audit --> rerank
Loading

Results

# offline (CI default)
explicit slice (C.0.5 resolver): 5/5 — gate 100%: PASS
semantic retriever (C.1): wired; recall@k needs --use_live_embeddings
correct overall (semantic path still stubbed): 10/319

# live (local, against a populated cache DB + embedding model)
retrieval recall@20 (live, 292 positive rows): any-hit 285/292 (98%), all-hit 274/292 (94%)
  • 82 librarian tests passing (66 from W1+W2 + 16 new)
  • both backends return the top-K shortlist rank-ordered by cosine, score_vector set, reranked[] empty (W4 fills it)

Live recall@20 measured against a populated cache DB (standards_cache.sqlite, 428 CRE embeddings, gemini/gemini-embedding-001, dim 3072), hub-firewall ON: any-hit 285/292 (98%), all-hit 274/292 (94%). Recall@k is the right W3 metric (not the Jaccard link rule, which grades the final auto-link and needs the W4 reranker): it asks the one question the search step controls — does the expected CRE id reach the shortlist the reranker will see? A miss here is unrecoverable downstream.

What is intentionally not here

The cross-encoder rerank (C.2, W4), decision/threshold routing (C.3–C.4, W5), SafetyGuard (W5), and graph writes (W8) are later weeks. reranked[] is therefore empty, and the pipeline is dry-run only.

The pgvector schema migration is intentionally a W8 deliverable. PgVectorRetriever + the build_retriever factory + the benchmark ship here (the W3 code deliverable), fully unit-tested via a fake connection. But the embedding_vec column it queries is created by an Alembic migration that the deliverables plan schedules for W8 (live integration) — where it can be validated against real Postgres (flask db upgrade/downgrade, make alembic-guardrail, backfill, ivfflat tuning). Until then pgvector is selectable by config but only runnable once W8 lands the column; in_memory is the default and the only backend used on SQLite/CI. Shipping the retriever code ahead of the schema keeps this PR free of an unvalidated shared-table migration.

How to verify locally

# 82 librarian tests (both backends + factory, all hermetic)
python3 -m unittest discover -s application/tests/librarian -p '*_test.py' -t .

# offline harness: C.0 pass rate + explicit gate + retriever wired
python3 scripts/evaluate_librarian.py --dataset application/tests/librarian/fixtures/golden_dataset.json

# live recall@20 (needs a populated cache DB + an embedding-capable LLM)
python3 scripts/evaluate_librarian.py \
    --dataset application/tests/librarian/fixtures/golden_dataset.json \
    --use_live_embeddings --cache_file standards_cache.sqlite

# run the pipeline switch end-to-end (dry run, logs the shortlist per section)
python3 cre.py --librarian_dry_run --cache_file standards_cache.sqlite

# backend benchmark (pgvector leg skips unless Postgres + embedding_vec present)
python3 scripts/benchmark_retriever.py --cache_file standards_cache.sqlite

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds Module C (“The Librarian”) with RFC-aligned schemas and Pydantic models, environment-based configuration, C.0 section validation, explicit CRE resolution, semantic retrieval, evaluation utilities, golden dataset tooling, and CLI integration.

Changes

Module C — The Librarian

Layer / File(s) Summary
RFC schemas and models
application/utils/librarian/__init__.py, application/utils/librarian/_rfc_schemas/*.json, application/utils/librarian/schemas.py, requirements.txt, application/tests/librarian/schemas_test.py
Vendored Draft 2020-12 JSON Schemas and mirrored Pydantic v2 models for RFC envelopes, internal queue rows, and golden dataset rows; pydantic is constrained to >=2,<3, and canonical-schema round-trip tests cover the contract models.
Librarian config and env defaults
.env.example, application/utils/librarian/config_loader.py, application/tests/librarian/config_loader_test.py
Frozen LibrarianConfig, load_config() for CRE_LIBRARIAN_* variables, env defaults, and tests for defaults, overrides, immutability, and invalid values.
C.0 section validation and explicit CRE fast path
application/utils/librarian/section_validator.py, application/utils/librarian/knowledge_source.py, application/tests/librarian/fixtures/sample_knowledge_queue.jsonl, application/tests/librarian/section_validator_test.py, application/utils/librarian/explicit_link_resolver.py, application/tests/librarian/explicit_link_resolver_test.py
Section validation for queue rows and knowledge items, fixture-backed knowledge sourcing, and deterministic explicit CRE extraction/resolution with tests for validation and resolution outcomes.
C.1 semantic candidate retrieval
application/utils/librarian/candidate_retriever.py, application/tests/librarian/candidate_retriever_test.py, scripts/benchmark_retriever.py
In-memory cosine and pgvector-backed retrieval, candidate-pool validation, retriever factory wiring, backend-specific tests, and a benchmark script that times both backends and compares top-1 agreement.
Hub firewall and scoring
application/utils/librarian/hub_firewall.py, application/utils/librarian/scoring.py, application/tests/librarian/hub_firewall_test.py, application/tests/librarian/scoring_test.py
Hub leakage detection/filtering and Jaccard-based scoring, with tests covering normalization and score boundaries.
Golden dataset tooling and schema checks
scripts/build_golden_dataset.py, application/tests/librarian/fixtures/golden_dataset.schema.json, application/tests/librarian/dataset_test.py
Deterministic golden dataset generation, dataset JSON Schema, shape/determinism tests, and committed dataset drift checks.
CLI wiring and evaluation scripts
cre.py, application/cmd/cre_main.py, scripts/evaluate_librarian.py
CLI flags for Module C, run_librarian() wiring, and the end-to-end evaluation harness that validates sections, gates explicit resolution, and reports recall.

Estimated code review effort: 5 (Critical) | ~120 minutes

Suggested reviewers: Pa04rth

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: the C.1 candidate retriever and pipeline switch.
Description check ✅ Passed The description is on-topic and accurately describes the retriever, CLI wiring, tests, and benchmark changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PRAteek-singHWY PRAteek-singHWY marked this pull request as ready for review June 18, 2026 13:51

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
application/utils/librarian/knowledge_source.py (1)

29-35: ⚠️ Potential issue | 🟠 Major

Wrap model_validate_json in error handling to prevent a single malformed line from aborting the entire run.

At line 34, an uncaught ValidationError from model_validate_json will propagate out of the generator and terminate the iteration in cre_main, causing the entire run to fail. The codebase already handles similar validation errors elsewhere (e.g., section_validator.py catches ValidationError and continues); apply the same pattern here by catching the exception and logging a warning before continuing to the next line.

Suggested fix
+import logging
 import json
 from abc import ABC, abstractmethod
 from typing import Iterator
 
+from pydantic import ValidationError
+
 from application.utils.librarian.schemas import KnowledgeQueueItem
 
+logger = logging.getLogger(__name__)
+
@@
     def items(self) -> Iterator[KnowledgeQueueItem]:
         with open(self._path, encoding="utf-8") as fh:
-            for line in fh:
+            for line_no, line in enumerate(fh, start=1):
                 line = line.strip()
                 if line:
-                    yield KnowledgeQueueItem.model_validate_json(line)
+                    try:
+                        yield KnowledgeQueueItem.model_validate_json(line)
+                    except ValidationError as exc:
+                        logger.warning(
+                            "Skipping malformed knowledge_queue row at line %d: %s",
+                            line_no,
+                            exc,
+                        )
+                        continue
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/librarian/knowledge_source.py` around lines 29 - 35, The
items method in the KnowledgeSource class does not handle ValidationError
exceptions that can be raised by model_validate_json when processing malformed
lines. Wrap the KnowledgeQueueItem.model_validate_json call in a try-except
block to catch ValidationError, log a warning message when validation fails for
a particular line, and use continue to skip that line and proceed to the next
one instead of allowing the exception to propagate and terminate the entire
iteration. This pattern is already implemented elsewhere in the codebase such as
in section_validator.py and should be applied consistently here.
🧹 Nitpick comments (2)
application/utils/librarian/__init__.py (1)

12-12: ⚡ Quick win

Update this scope note to reflect current module reality.

Line 12 says there is “No linking logic yet,” but this PR stack now includes explicit resolution and semantic retrieval in Module C. Keeping this sentence stale can mislead future maintainers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/librarian/__init__.py` at line 12, Update the scope
documentation at line 12 in the __init__.py file to reflect the current state of
the librarian module. Remove or revise the statement that says "No linking logic
yet" since the PR stack now includes explicit resolution and semantic retrieval
functionality in Module C. Update the scope note to accurately describe what the
module currently contains and supports.
application/tests/librarian/config_loader_test.py (1)

12-19: ⚡ Quick win

Add retriever_backend assertions to complete the config contract checks.

The default and override tests validate most fields but skip retriever_backend, which is part of LibrarianConfig and a key routing control.

Proposed test additions
 class TestConfigLoaderDefaults(unittest.TestCase):
@@
     def test_defaults_when_env_unset(self):
         with mock.patch.dict(os.environ, {}, clear=True):
             cfg = load_config()
+        self.assertEqual(cfg.retriever_backend, "in_memory")
         self.assertEqual(cfg.crossencoder_model, "cross-encoder/ms-marco-MiniLM-L-6-v2")
@@
 class TestConfigLoaderOverrides(unittest.TestCase):
     OVERRIDES = {
+        "CRE_LIBRARIAN_RETRIEVER_BACKEND": "pgvector",
         "CRE_LIBRARIAN_CROSSENCODER_MODEL": "cross-encoder/other",
@@
     def test_env_overrides_apply(self):
         with mock.patch.dict(os.environ, self.OVERRIDES, clear=True):
             cfg = load_config()
+        self.assertEqual(cfg.retriever_backend, "pgvector")
         self.assertEqual(cfg.crossencoder_model, "cross-encoder/other")

Also applies to: 27-35, 40-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/tests/librarian/config_loader_test.py` around lines 12 - 19, The
test assertions for the configuration loader are incomplete and missing
validation for the `retriever_backend` field, which is a key part of the
LibrarianConfig contract. Add self.assertEqual assertions to verify the
`retriever_backend` field matches the expected value in both the default
configuration test and the override configuration tests (including the sections
at lines 27-35 and 40-47), following the same pattern used for other fields like
crossencoder_model, top_k_retrieval, batch_size, and conformal_alpha.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@application/tests/librarian/config_loader_test.py`:
- Around line 20-23: The test_config_is_frozen method has two issues: it doesn't
isolate os.environ when calling load_config(), which can cause spurious failures
if local CRE_LIBRARIAN_* environment variables are set (other tests in the same
file correctly use mock.patch.dict(os.environ, {}, clear=True)), and it uses
assertRaises(Exception) which is too broad and can mask unrelated failures. Wrap
the load_config() call with mock.patch.dict(os.environ, {}, clear=True) to
isolate the environment, and change assertRaises(Exception) to
assertRaises(FrozenInstanceError) from the dataclasses module to assert the
specific exception that should be raised when attempting to modify a frozen
dataclass instance.

In `@application/tests/librarian/dataset_test.py`:
- Around line 110-114: Add a timeout parameter to the subprocess.run() call that
executes the determinism check with _BUILD_SCRIPT and "--check" arguments. This
will prevent the test from hanging indefinitely if the subprocess stalls due to
database locks or I/O issues. Include an appropriate timeout value (in seconds)
in the subprocess.run() function to ensure the test fails fast rather than
blocking the entire test suite.

In `@application/utils/librarian/schemas.py`:
- Line 67: The Pydantic model fields are currently using plain Optional[str]
types which do not enforce RFC format validation, allowing invalid payloads
through and breaking contract parity. Fix this by updating six fields across the
models: SourceRef.url and SourceRef.committed_at, Locator.url,
KnowledgeItem.filtered_at, LinkProposal.classified_at, and
ReviewItem.created_at. Replace all URI fields (url and similar) with AnyUrl type
imported from pydantic module, and replace all date-time fields (committed_at,
filtered_at, classified_at, created_at) with datetime type imported from the
standard library. Pydantic v2 will natively validate RFC 3339 format for
datetime fields and enforce valid URI schemes for AnyUrl fields, maintaining RFC
schema contract requirements.

In `@application/utils/librarian/section_validator.py`:
- Around line 163-166: Replace the assert statement that checks if item.content
is not None with an explicit if check followed by raising
MalformedKnowledgeItemError. The assert statement on line 164 gets stripped
during optimized Python execution (python -O), causing subsequent calls to
item.content.text and item.content.language in _require_text() and
_require_language() to fail with AttributeError instead of the expected typed
exception. Use an if statement that validates item.content is not None, and if
it fails, raise MalformedKnowledgeItemError with an appropriate message about
missing content to ensure consistent validation behavior and error reporting at
this boundary regardless of Python optimization flags.

In `@scripts/build_golden_dataset.py`:
- Line 261: The loop variable `node_id` in the for loop that iterates over
`rows` is never used in the loop body, which triggers a B007 lint error. Rename
this unused loop variable to an underscore `_` to indicate that it is
intentionally unused and to comply with coding guidelines, ensuring that the
loop still correctly unpacks all the values from each row tuple.

---

Outside diff comments:
In `@application/utils/librarian/knowledge_source.py`:
- Around line 29-35: The items method in the KnowledgeSource class does not
handle ValidationError exceptions that can be raised by model_validate_json when
processing malformed lines. Wrap the KnowledgeQueueItem.model_validate_json call
in a try-except block to catch ValidationError, log a warning message when
validation fails for a particular line, and use continue to skip that line and
proceed to the next one instead of allowing the exception to propagate and
terminate the entire iteration. This pattern is already implemented elsewhere in
the codebase such as in section_validator.py and should be applied consistently
here.

---

Nitpick comments:
In `@application/tests/librarian/config_loader_test.py`:
- Around line 12-19: The test assertions for the configuration loader are
incomplete and missing validation for the `retriever_backend` field, which is a
key part of the LibrarianConfig contract. Add self.assertEqual assertions to
verify the `retriever_backend` field matches the expected value in both the
default configuration test and the override configuration tests (including the
sections at lines 27-35 and 40-47), following the same pattern used for other
fields like crossencoder_model, top_k_retrieval, batch_size, and
conformal_alpha.

In `@application/utils/librarian/__init__.py`:
- Line 12: Update the scope documentation at line 12 in the __init__.py file to
reflect the current state of the librarian module. Remove or revise the
statement that says "No linking logic yet" since the PR stack now includes
explicit resolution and semantic retrieval functionality in Module C. Update the
scope note to accurately describe what the module currently contains and
supports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a0a9bece-b59f-4c00-a238-d4dd2b76ad18

📥 Commits

Reviewing files that changed from the base of the PR and between e853cd3 and 73f6a52.

📒 Files selected for processing (34)
  • .env.example
  • application/cmd/cre_main.py
  • application/tests/librarian/__init__.py
  • application/tests/librarian/candidate_retriever_test.py
  • application/tests/librarian/config_loader_test.py
  • application/tests/librarian/dataset_test.py
  • application/tests/librarian/explicit_link_resolver_test.py
  • application/tests/librarian/fixtures/golden_dataset.json
  • application/tests/librarian/fixtures/golden_dataset.schema.json
  • application/tests/librarian/fixtures/sample_knowledge_queue.jsonl
  • application/tests/librarian/hub_firewall_test.py
  • application/tests/librarian/schemas_test.py
  • application/tests/librarian/scoring_test.py
  • application/tests/librarian/section_validator_test.py
  • application/utils/librarian/__init__.py
  • application/utils/librarian/_rfc_schemas/knowledge-item.json
  • application/utils/librarian/_rfc_schemas/link-proposal.json
  • application/utils/librarian/_rfc_schemas/locator.json
  • application/utils/librarian/_rfc_schemas/proposed-link.json
  • application/utils/librarian/_rfc_schemas/review-item.json
  • application/utils/librarian/_rfc_schemas/source-ref.json
  • application/utils/librarian/candidate_retriever.py
  • application/utils/librarian/config_loader.py
  • application/utils/librarian/explicit_link_resolver.py
  • application/utils/librarian/hub_firewall.py
  • application/utils/librarian/knowledge_source.py
  • application/utils/librarian/schemas.py
  • application/utils/librarian/scoring.py
  • application/utils/librarian/section_validator.py
  • cre.py
  • requirements.txt
  • scripts/benchmark_retriever.py
  • scripts/build_golden_dataset.py
  • scripts/evaluate_librarian.py

Comment thread application/tests/librarian/config_loader_test.py
Comment thread application/tests/librarian/dataset_test.py
Comment thread application/utils/librarian/schemas.py Outdated
Comment thread application/utils/librarian/section_validator.py Outdated
Comment thread scripts/build_golden_dataset.py Outdated
PRAteek-singHWY added a commit to PRAteek-singHWY/OpenCRE that referenced this pull request Jun 26, 2026
Resolve the CodeRabbit review comments on the Module C PR:
- schemas.py: enforce RFC format parity — AnyUrl for url fields,
  datetime for committed_at/filtered_at/classified_at/created_at
- section_validator.py: replace assert with a typed
  MalformedKnowledgeItemError guard (survives python -O)
- knowledge_source.py: skip+log malformed JSONL rows instead of
  aborting the whole iteration on ValidationError
- config_loader_test.py: isolate os.environ and assert the specific
  FrozenInstanceError
- dataset_test.py: add a timeout to the determinism subprocess call
- build_golden_dataset.py: rename unused loop var node_id -> _node_id
- section_validator_test.py: assert committed_at as the typed datetime

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PRAteek-singHWY added a commit to PRAteek-singHWY/OpenCRE that referenced this pull request Jun 26, 2026
Resolve the CodeRabbit review comments on the Module C PR:
- schemas.py: enforce RFC format parity — AnyUrl for url fields,
  datetime for committed_at/filtered_at/classified_at/created_at
- section_validator.py: replace assert with a typed
  MalformedKnowledgeItemError guard (survives python -O)
- knowledge_source.py: skip+log malformed JSONL rows instead of
  aborting the whole iteration on ValidationError
- config_loader_test.py: isolate os.environ and assert the specific
  FrozenInstanceError
- dataset_test.py: add a timeout to the determinism subprocess call
- build_golden_dataset.py: rename unused loop var node_id -> _node_id
- section_validator_test.py: assert committed_at as the typed datetime

@northdpole northdpole left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent W3 — C.1 is the strongest slice so far: clean DI seam (embed_fn + CandidatePool), dim gate, dual backends behind one retrieve()/RetrievalAudit, honest offline harness (no fake recall@k), and a dry-run CLI switch that doesn't write. Ready to merge once #922 and #925 land and this rebases (diff shrinks to the ~15 W3 files).

Before merge

  • Blocked on #922#925 — merge W1/W2 first, then rebase onto main.
  • Paste live recall@20 numbers into the PR description (positive slice, --use_live_embeddings). W3's gate is recall@k on the search step — the __/292 placeholders should be replaced with measured any-hit / all-hit before we merge the first ML component.

Please open / extend ticket

Harness gates in CI: extends the W2 ticket — subprocess coverage for evaluate_librarian (explicit 100% gate offline; optional live recall@k job with populated cache DB + API key, or documented manual pre-merge check).

Production note

First opt-in CLI touch (--run_librarian / --librarian_dry_run) — safe for Heroku as-is (not on Procfile). Document for ops: don't wire into worker until W8; embedding API cost only when someone runs the CLI manually.

Cross-module note

chunk_id synthesis from queue rows still differs from Module B ChangeRecord format — align at B→C pipeline wiring (not blocking W3).

Nit [optional]

pgvector backend selectable via env but column lands W8 — consider a loud startup warning when CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector on SQLite/non-Postgres.

Ping when recall numbers are in and rebased; happy to merge right after.

PRAteek-singHWY added a commit to PRAteek-singHWY/OpenCRE that referenced this pull request Jul 2, 2026
…n-Postgres

Maintainer nit on OWASP#937: CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector is selectable
via env, but the embedding_vec column doesn't land until W8. Emit a loud
startup warning at pipeline-switch time when the backend is pgvector on a
non-Postgres (e.g. SQLite) database, since retrieve() would otherwise fail
with an opaque SQL error.
PRAteek-singHWY added a commit to PRAteek-singHWY/OpenCRE that referenced this pull request Jul 3, 2026
- config_loader_test: assert retriever_backend in both the defaults and the
  override tests (was the only LibrarianConfig field left unchecked).
- __init__.py: refresh the stale 'No linking logic yet' scope note to reflect
  W1 contracts + W2 C.0 boundary + W3 C.1 retriever.

Other OWASP#937 CodeRabbit findings were already addressed on this branch
(knowledge_source model_validate_json error handling, schemas AnyUrl/datetime
fields, section_validator assert->if, test_config_is_frozen isolation +
FrozenInstanceError, dataset_test subprocess timeout, build_golden_dataset
unused loop var).
PRAteek-singHWY added a commit to PRAteek-singHWY/OpenCRE that referenced this pull request Jul 3, 2026
Address northdpole's production note on OWASP#937: record at the run_librarian
entrypoint that it is opt-in CLI only (not on Procfile, not wired into web or
worker until W8) and that the paid embedding API cost is incurred only on manual
runs, never by the running deployment.
…ipeline switch

The semantic search step. For sections with no explicit CRE id, embed the
text and cosine-rank the CRE vector hub, returning the top-K (default 20)
candidates as an RFC RetrievalAudit (reranked[] empty until W4).

Two interchangeable backends behind one retrieve() seam, selected by
CRE_LIBRARIAN_RETRIEVER_BACKEND:
- CandidateRetriever: in-memory sklearn cosine (SQLite/CI/harness)
- PgVectorRetriever: Postgres-side <=> cosine over embedding_vec
build_retriever() is the factory. Reuses OpenCRE's embedding stack
(PromptHandler.get_text_embeddings, db.get_embeddings_by_doc_type).

Dim gate fails loudly on empty/ragged hubs and query/hub width mismatch.
CLI switch (--run_librarian / --librarian_dry_run) runs the pipeline
dry-run; harness --use_live_embeddings measures retrieval recall@k.

The pgvector schema migration is a W8 deliverable (validated against real
Postgres there); W3 ships only the code path, unit-tested via a fake
connection. 82 librarian tests pass.
Resolve the CodeRabbit review comments on the Module C PR:
- schemas.py: enforce RFC format parity — AnyUrl for url fields,
  datetime for committed_at/filtered_at/classified_at/created_at
- section_validator.py: replace assert with a typed
  MalformedKnowledgeItemError guard (survives python -O)
- knowledge_source.py: skip+log malformed JSONL rows instead of
  aborting the whole iteration on ValidationError
- config_loader_test.py: isolate os.environ and assert the specific
  FrozenInstanceError
- dataset_test.py: add a timeout to the determinism subprocess call
- build_golden_dataset.py: rename unused loop var node_id -> _node_id
- section_validator_test.py: assert committed_at as the typed datetime
…n-Postgres

Maintainer nit on OWASP#937: CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector is selectable
via env, but the embedding_vec column doesn't land until W8. Emit a loud
startup warning at pipeline-switch time when the backend is pgvector on a
non-Postgres (e.g. SQLite) database, since retrieve() would otherwise fail
with an opaque SQL error.
- config_loader_test: assert retriever_backend in both the defaults and the
  override tests (was the only LibrarianConfig field left unchecked).
- __init__.py: refresh the stale 'No linking logic yet' scope note to reflect
  W1 contracts + W2 C.0 boundary + W3 C.1 retriever.

Other OWASP#937 CodeRabbit findings were already addressed on this branch
(knowledge_source model_validate_json error handling, schemas AnyUrl/datetime
fields, section_validator assert->if, test_config_is_frozen isolation +
FrozenInstanceError, dataset_test subprocess timeout, build_golden_dataset
unused loop var).
Address northdpole's production note on OWASP#937: record at the run_librarian
entrypoint that it is opt-in CLI only (not on Procfile, not wired into web or
worker until W8) and that the paid embedding API cost is incurred only on manual
runs, never by the running deployment.
…l@20 is measurable

The embeddings pool is keyed by the CRE internal UUID, but the golden
dataset expects external_ids (e.g. 616-305). Without translating, every
comparison missed and recall@20 read 0%. Map pool keys via cre.id->
external_id (pass-through for DBs already keyed by external_id).
@northdpole northdpole force-pushed the gsocmodule_C_week_3 branch from 5e95496 to 87103b5 Compare July 9, 2026 13:12

@northdpole northdpole left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved after rebase onto main (#925 merged). W3 retriever + pipeline switch looks good.

@northdpole northdpole merged commit b8b677c into OWASP:main Jul 9, 2026
6 checks passed
northdpole pushed a commit that referenced this pull request Jul 9, 2026
Resolve the CodeRabbit review comments on the Module C PR:
- schemas.py: enforce RFC format parity — AnyUrl for url fields,
  datetime for committed_at/filtered_at/classified_at/created_at
- section_validator.py: replace assert with a typed
  MalformedKnowledgeItemError guard (survives python -O)
- knowledge_source.py: skip+log malformed JSONL rows instead of
  aborting the whole iteration on ValidationError
- config_loader_test.py: isolate os.environ and assert the specific
  FrozenInstanceError
- dataset_test.py: add a timeout to the determinism subprocess call
- build_golden_dataset.py: rename unused loop var node_id -> _node_id
- section_validator_test.py: assert committed_at as the typed datetime
northdpole pushed a commit that referenced this pull request Jul 9, 2026
…n-Postgres

Maintainer nit on #937: CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector is selectable
via env, but the embedding_vec column doesn't land until W8. Emit a loud
startup warning at pipeline-switch time when the backend is pgvector on a
non-Postgres (e.g. SQLite) database, since retrieve() would otherwise fail
with an opaque SQL error.
northdpole pushed a commit that referenced this pull request Jul 9, 2026
- config_loader_test: assert retriever_backend in both the defaults and the
  override tests (was the only LibrarianConfig field left unchecked).
- __init__.py: refresh the stale 'No linking logic yet' scope note to reflect
  W1 contracts + W2 C.0 boundary + W3 C.1 retriever.

Other #937 CodeRabbit findings were already addressed on this branch
(knowledge_source model_validate_json error handling, schemas AnyUrl/datetime
fields, section_validator assert->if, test_config_is_frozen isolation +
FrozenInstanceError, dataset_test subprocess timeout, build_golden_dataset
unused loop var).
northdpole pushed a commit that referenced this pull request Jul 9, 2026
Address northdpole's production note on #937: record at the run_librarian
entrypoint that it is opt-in CLI only (not on Procfile, not wired into web or
worker until W8) and that the paid embedding API cost is incurred only on manual
runs, never by the running deployment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants