Skip to content

Commit f41410a

Browse files
authored
[RAG Projection] Add revision-aware derived indexing and rebuilds (#6)
Refs #3
1 parent 688a10e commit f41410a

16 files changed

Lines changed: 4068 additions & 197 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ jobs:
162162
generic_rag \
163163
generic_rag.errors \
164164
generic_rag.contracts \
165-
generic_rag.ports
165+
generic_rag.ports \
166+
generic_rag.projection
166167
do
167168
(
168169
cd "$rag_probe_dir"

PYTHON_MODULE_INDEX.md

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
- Declared source root: `src`
66
- Packaging source of truth: `pyproject.toml`
7-
- Importable production units: 4
8-
- Indexed production units: 4
9-
- Source/index parity: 4/4
7+
- Importable production units: 5
8+
- Indexed production units: 5
9+
- Source/index parity: 5/5
1010
- Package data: `src/generic_rag/py.typed`
1111
- Locked verification owner: `.github/workflows/ci.yml` (supporting workflow,
1212
not an importable unit)
@@ -41,9 +41,9 @@
4141
- Owned state or external resources: none.
4242
- Material side effects: none.
4343
- Verification: `tests/test_errors.py`, `tests/test_package_boundaries.py`,
44-
`tests/support/clean_import_probe.py`, and the locked CI import and boundary
45-
checks.
46-
- Documentation: `docs/api.md`.
44+
`tests/test_projection.py`, `tests/support/clean_import_probe.py`, and the
45+
locked CI import and boundary checks.
46+
- Documentation: `docs/api.md` and `docs/projection.md`.
4747

4848
## `generic_rag.contracts`
4949

@@ -53,37 +53,68 @@
5353
- Supported public imports: `DocumentKey`, `DocumentIdentity`, `Document`,
5454
`FragmentIdentity`, `Fragment`, `EmbeddingIdentity`, `EmbeddingVector`,
5555
`VectorRecord`, `ProjectionIdentity`, `ProjectionCheckpoint`,
56-
`ProjectionOutcome`, `ProjectionReceipt`, `RetrievalQuery`,
57-
`RetrievalOutcome`, `RetrievalHit`, and `RetrievalResult` from
58-
`generic_rag.contracts`.
56+
`ProjectionOutcome`, `ProjectionReceipt`, `ChunkingPolicy`,
57+
`ProjectionLimits`, `ProjectionRequest`, `ProjectionManifestEntry`,
58+
`ProjectionManifest`, `ProjectionStateAvailability`,
59+
`ProjectionStateSnapshot`, `ProjectionStateStatus`, `ProjectionResult`,
60+
`RetrievalQuery`, `RetrievalOutcome`, `RetrievalHit`, and `RetrievalResult`
61+
from `generic_rag.contracts`.
5962
- Re-exports: exactly the names in the module's `__all__`; none from the package
6063
root.
6164
- Direct internal dependencies: `generic_rag.errors`.
6265
- Owned state or external resources: none; instances own only immutable caller
6366
values.
6467
- Material side effects: none.
6568
- Verification: `tests/test_contract_values.py`,
66-
`tests/test_projection_contracts.py`, `tests/test_retrieval_contracts.py`,
67-
`tests/test_package_boundaries.py`, `tests/support/clean_import_probe.py`,
68-
`tests/support/verify_artifacts.py`, and the locked CI import, boundary, and
69-
artifact checks.
70-
- Documentation: `docs/api.md` and `docs/security-and-privacy.md`.
69+
`tests/test_projection_contracts.py`, `tests/test_projection.py`,
70+
`tests/test_retrieval_contracts.py`, `tests/test_package_boundaries.py`,
71+
`tests/support/clean_import_probe.py`, `tests/support/verify_artifacts.py`, and
72+
the locked CI import, boundary, and artifact checks.
73+
- Documentation: `docs/api.md`, `docs/projection.md`, and
74+
`docs/security-and-privacy.md`.
7175

7276
## `generic_rag.ports`
7377

7478
- Source: `src/generic_rag/ports.py`
7579
- Responsibility: define synchronous injected collaborator interfaces and
7680
explicit caller-owned borrowing semantics.
7781
- Supported public imports: `Borrowed`, `Embedder`, `VectorIndexWriter`,
78-
`VectorIndexReader`, and `LexicalRetriever` from `generic_rag.ports`.
82+
`VectorIndexResetter`, `VectorIndexReader`, and `LexicalRetriever` from
83+
`generic_rag.ports`.
7984
- Re-exports: exactly the names in the module's `__all__`; none from the package
8085
root.
8186
- Direct internal dependencies: `generic_rag.contracts`.
8287
- Owned state or external resources: `Borrowed` retains a reference but never
8388
owns, acquires, releases, closes, or shuts down the resource.
8489
- Material side effects: none.
8590
- Verification: `tests/test_ports.py`, `tests/test_package_boundaries.py`,
86-
`tests/support/clean_import_probe.py`, and the locked CI import and boundary
91+
`tests/test_projection.py`, `tests/support/clean_import_probe.py`, and the
92+
locked CI import and boundary checks.
93+
- Documentation: `docs/api.md`, `docs/lifecycle.md`, `docs/projection.md`, and
94+
`docs/security-and-privacy.md`.
95+
96+
## `generic_rag.projection`
97+
98+
- Source: `src/generic_rag/projection.py`
99+
- Responsibility: deterministically plan and synchronously execute bounded,
100+
revision-aware document projection against caller-supplied state.
101+
- Supported public imports: `ProjectionFailureStage`, `ProjectionStateError`,
102+
`ProjectionOperationError`, `project_documents`, and `rebuild_projection`
103+
from `generic_rag.projection`.
104+
- Re-exports: exactly the names in the module's `__all__`; none from the package
105+
root.
106+
- Direct internal dependencies: `generic_rag.contracts`, `generic_rag.errors`,
107+
and `generic_rag.ports`.
108+
- Owned state or external resources: none; planning state is immutable and
109+
local to each call, while every embedder, writer, and resetter remains
110+
caller-owned through `Borrowed`.
111+
- Material side effects: none at import time. At explicit workflow call time it
112+
may invoke the borrowed embedder and vector writer, and full rebuild may
113+
invoke the borrowed corpus resetter; it performs no persistence, network,
114+
retry, acquisition, release, or lifecycle action itself.
115+
- Verification: `tests/test_projection.py`, `tests/test_package_boundaries.py`,
116+
`tests/support/clean_import_probe.py`, `tests/support/verify_artifacts.py`, and
117+
the locked CI test, lint, type, build, clean-install, import, and artifact
87118
checks.
88-
- Documentation: `docs/api.md`, `docs/lifecycle.md`, and
119+
- Documentation: `docs/projection.md`, `docs/api.md`, `docs/lifecycle.md`, and
89120
`docs/security-and-privacy.md`.

README.md

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
`generic-rag` is a provider-neutral, runtime-dependency-free foundation for
44
retrieval-augmented generation (RAG). Version 0.1.0 requires Python 3.11 or
55
later and provides immutable contracts, typed error categories, synchronous
6-
collaborator protocols, and explicit caller-owned borrowing.
6+
collaborator protocols, deterministic bounded projection orchestration, and
7+
explicit caller-owned borrowing.
78

8-
No projection or retrieval algorithm is implemented in 0.1.0. The package has
9+
Retrieval and result composition are not implemented in 0.1.0. The package has
910
no built-in adapter, provider, factory, persistence, network client,
1011
configuration system, authentication, citation mechanism, or CLI.
1112

@@ -21,87 +22,125 @@ python -m pip install .
2122
The installed package has no runtime dependencies. Build and development tools
2223
are separate locked dependency groups.
2324

24-
## Use the contracts in application code
25+
## Project approved documents
2526

2627
The host application remains responsible for authorization and policy checks.
27-
After approving a source and query, application code can construct generic
28-
values and accept an application-owned provider through a protocol:
28+
After approving source content, construct a complete bounded target, wrap
29+
application-owned adapters in `Borrowed`, and call a projection workflow:
2930

3031
```python
3132
from generic_rag.contracts import (
33+
ChunkingPolicy,
3234
Document,
3335
DocumentIdentity,
3436
DocumentKey,
37+
EmbeddingIdentity,
3538
EmbeddingVector,
36-
RetrievalQuery,
39+
ProjectionIdentity,
40+
ProjectionLimits,
41+
ProjectionRequest,
42+
ProjectionStateAvailability,
43+
ProjectionStateSnapshot,
3744
)
38-
from generic_rag.ports import Borrowed, Embedder
45+
from generic_rag.ports import Borrowed
46+
from generic_rag.projection import rebuild_projection
3947

40-
# Construct these values only after application-specific authorization.
41-
approved_document = Document(
42-
identity=DocumentIdentity(
43-
key=DocumentKey(corpus_id="corpus-a", document_id="document-1"),
44-
revision_id="revision-3",
48+
class ExampleEmbedder:
49+
identity = EmbeddingIdentity("example-model", 2)
50+
51+
def embed(self, texts, /):
52+
return tuple(EmbeddingVector((float(len(text)), 0.0)) for text in texts)
53+
54+
55+
class ExampleWriter:
56+
def replace_document(self, document, records, /):
57+
# Replace the complete projection for this stable document key.
58+
return None
59+
60+
def delete_document(self, document, /):
61+
return None
62+
63+
64+
class ExampleResetter:
65+
def reset_corpus(self, corpus_id, /):
66+
# Remove every projected document for this corpus.
67+
return None
68+
69+
70+
request = ProjectionRequest(
71+
"corpus-a",
72+
ProjectionIdentity("schema-v1", ExampleEmbedder.identity),
73+
ChunkingPolicy(max_fragment_codepoints=800, overlap_codepoints=80),
74+
ProjectionLimits(
75+
max_documents=100,
76+
max_document_codepoints=100_000,
77+
max_embedding_batch_size=32,
78+
),
79+
(
80+
Document(
81+
DocumentIdentity(
82+
DocumentKey("corpus-a", "document-1"),
83+
"revision-3",
84+
),
85+
"Approved source text",
86+
(("classification", "internal"),),
87+
),
4588
),
46-
text="Approved source text",
47-
attributes=(("classification", "internal"),),
48-
)
49-
query = RetrievalQuery(
50-
corpus_id=approved_document.identity.key.corpus_id,
51-
text="What does the source say?",
52-
hit_limit=5,
53-
candidate_limit=20,
5489
)
5590

91+
# Bootstrap and recovery are explicit and destructive: reset, then replace.
92+
result = rebuild_projection(
93+
request,
94+
ProjectionStateSnapshot(ProjectionStateAvailability.MISSING, None),
95+
Borrowed(ExampleEmbedder()),
96+
Borrowed(ExampleWriter()),
97+
Borrowed(ExampleResetter()),
98+
)
5699

57-
def application_embed_query(
58-
provider: Embedder,
59-
request: RetrievalQuery,
60-
) -> EmbeddingVector:
61-
with Borrowed(provider) as embedder:
62-
vectors = embedder.embed((request.text,))
63-
if len(vectors) != 1:
64-
raise ValueError("the provider violated the Embedder contract")
65-
return vectors[0]
100+
# Persist result.manifest in application-owned state only after success.
66101
```
67102

68-
This is application orchestration, not a package retrieval workflow. Version
69-
0.1.0 defines the boundary that provider implementations and later generic
70-
workflows will use; it does not construct providers or call them on a user's
71-
behalf.
103+
For normal updates, load that manifest into a present
104+
`ProjectionStateSnapshot` and call `project_documents`; it changes only added,
105+
updated, removed, or rechunked documents. Use `rebuild_projection` only when an
106+
explicit corpus-wide reset is intended. See the [projection guide](docs/projection.md)
107+
for the complete lifecycle, state matrix, adapter obligations, and failure
108+
behavior.
72109

73110
Public values must be imported from their owning modules:
74111

75112
- `generic_rag.contracts`
76113
- `generic_rag.errors`
77114
- `generic_rag.ports`
115+
- `generic_rag.projection`
78116

79117
The package root intentionally has no re-exports: `generic_rag.__all__ == ()`.
80118
See the [API reference](docs/api.md) for every supported name and invariant.
81119

82-
## Planned RAG flow
120+
## Application RAG flow
83121

84122
The package itself has no concept of a user or agent. A consuming application
85123
decides which sources a user may approve, which queries may be submitted, which
86124
provider implementations receive data, and whether retrieved fragments are
87125
shown to a user or supplied to a downstream tool or agent.
88126

89-
- [Issue #3](https://github.com/Kims-DeveloperGroup/generic-rag/issues/3) is
90-
planned to add generic projection orchestration. Its intended responsibility
91-
is to accept caller-approved documents and explicitly injected collaborators,
92-
derive fragments under a defined chunking policy, embed ordered fragment
93-
text, replace or delete complete document projections, and report truthful
94-
checkpoints and receipts. Its precise API and failure behavior are not part
95-
of 0.1.0.
127+
- Projection accepts caller-approved documents and explicitly injected
128+
collaborators. It derives deterministic fragments under a bounded chunking
129+
policy, embeds ordered fragment text, replaces or deletes complete document
130+
projections, and returns a manifest and truthful receipt for caller-owned
131+
persistence.
96132
- [Issue #4](https://github.com/Kims-DeveloperGroup/generic-rag/issues/4) is
97133
planned to add retrieval and composition. Its intended responsibility is to
98134
use an injected `Embedder` and `VectorIndexReader` for semantic candidates
99135
and an injected `LexicalRetriever` for lexical candidates, then define
100136
deduplication, fusion, limiting, and outcome behavior. Provider rank will be
101137
the input; raw provider scores are not represented or assumed comparable.
102138

103-
The caller/provider ownership model remains explicit throughout this plan. See
104-
[resource lifecycle](docs/lifecycle.md) and
139+
There is no end-user or agent query workflow yet. A consuming application can
140+
project data now, but must wait for or implement a separate reviewed retrieval
141+
layer before supplying retrieved context to users, tools, or agents. The
142+
caller/provider ownership model remains explicit throughout. See [resource
143+
lifecycle](docs/lifecycle.md) and
105144
[security and privacy](docs/security-and-privacy.md).
106145

107146
## Compatibility
@@ -111,9 +150,10 @@ assume compatibility across minor releases. For this release, direct imports
111150
from the documented owning modules are the supported public paths; root-level
112151
imports are not.
113152

114-
The distribution includes `py.typed`. The wheel contains exactly the four
153+
The distribution includes `py.typed`. The wheel contains exactly the five
115154
importable modules `generic_rag`, `generic_rag.errors`,
116-
`generic_rag.contracts`, and `generic_rag.ports`, plus the typing marker.
155+
`generic_rag.contracts`, `generic_rag.ports`, and `generic_rag.projection`, plus
156+
the typing marker.
117157

118158
## Development verification
119159

@@ -140,6 +180,5 @@ uv run --frozen python tests/support/verify_artifacts.py "$rag_dist_dir"
140180

141181
CI is configured to run the tests on Python 3.11 and 3.14. On Python 3.11 it
142182
also runs lint, format, strict type, compilation, artifact, source-rebuild,
143-
clean-install, and isolated-import checks. A local Python 3.14.2 run currently
144-
contains 53 passing tests; the CI matrix is the authoritative cross-version
145-
result.
183+
clean-install, and isolated-import checks. The CI matrix is the authoritative
184+
cross-version result.

0 commit comments

Comments
 (0)