sable-sphynx 0.7.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. sable_sphynx-0.7.0.dev0.dist-info/METADATA +152 -0
  2. sable_sphynx-0.7.0.dev0.dist-info/RECORD +174 -0
  3. sable_sphynx-0.7.0.dev0.dist-info/WHEEL +5 -0
  4. sable_sphynx-0.7.0.dev0.dist-info/top_level.txt +1 -0
  5. sphynx/__init__.py +469 -0
  6. sphynx/__init__.pyi +376 -0
  7. sphynx/_analytical_client.py +77 -0
  8. sphynx/_analytical_values.py +252 -0
  9. sphynx/_audit_client.py +173 -0
  10. sphynx/_comparison_adapters.py +113 -0
  11. sphynx/_componentized_full_rag.py +34 -0
  12. sphynx/_composed_pipeline.py +109 -0
  13. sphynx/_full_rag_grading.py +218 -0
  14. sphynx/_full_rag_trace.py +221 -0
  15. sphynx/_local_eval.py +268 -0
  16. sphynx/_pipeline_core.py +243 -0
  17. sphynx/_pr_client.py +237 -0
  18. sphynx/_rag_diagnostics_calibration.py +285 -0
  19. sphynx/_rag_diagnostics_catalog.py +39 -0
  20. sphynx/_rag_diagnostics_common.py +26 -0
  21. sphynx/_rag_diagnostics_contracts.py +186 -0
  22. sphynx/_rag_diagnostics_engine.py +243 -0
  23. sphynx/_rag_diagnostics_importers.py +242 -0
  24. sphynx/_rag_diagnostics_ranking.py +81 -0
  25. sphynx/_rag_diagnostics_semantic.py +325 -0
  26. sphynx/_rag_ranking.py +149 -0
  27. sphynx/_speech_benchmark_scoring.py +140 -0
  28. sphynx/_speech_benchmark_sources.py +275 -0
  29. sphynx/_speech_complete_scoring.py +218 -0
  30. sphynx/_speech_complete_source.py +118 -0
  31. sphynx/_speech_kido.py +294 -0
  32. sphynx/_speech_scoring.py +77 -0
  33. sphynx/_track_capture.py +213 -0
  34. sphynx/analytical_views.py +515 -0
  35. sphynx/assessment_contracts.py +378 -0
  36. sphynx/assessment_selection.py +57 -0
  37. sphynx/audit_access.py +245 -0
  38. sphynx/audit_contracts.py +204 -0
  39. sphynx/audit_requests.py +180 -0
  40. sphynx/authoring.py +188 -0
  41. sphynx/benchmarks.py +568 -0
  42. sphynx/candidates.py +62 -0
  43. sphynx/catalog.py +272 -0
  44. sphynx/chunking.py +372 -0
  45. sphynx/clickhouse_reader.py +169 -0
  46. sphynx/client.py +486 -0
  47. sphynx/comparison_core.py +149 -0
  48. sphynx/comparison_views.py +223 -0
  49. sphynx/components.py +1661 -0
  50. sphynx/contracts.py +514 -0
  51. sphynx/customer_rerank.py +299 -0
  52. sphynx/customer_retrieval.py +146 -0
  53. sphynx/data/benchmark-releases.v1.json +164 -0
  54. sphynx/data/public-benchmark-release.v1.json +354 -0
  55. sphynx/data/rag-diagnostics-synthetic.v1.json +6982 -0
  56. sphynx/data/rag-paper-references.v1.json +176 -0
  57. sphynx/data/rag-paper-schemas.v1.json +1069 -0
  58. sphynx/data/rag-rerank-v1.json +507 -0
  59. sphynx/dataset_catalog_data.py +1214 -0
  60. sphynx/dataset_readiness.py +138 -0
  61. sphynx/dataset_v3.py +999 -0
  62. sphynx/demo.py +130 -0
  63. sphynx/demo_candidates/__init__.py +1 -0
  64. sphynx/demo_candidates/dense.py +11 -0
  65. sphynx/demo_candidates/lexical.py +21 -0
  66. sphynx/diagnostics.py +207 -0
  67. sphynx/documentation.py +737 -0
  68. sphynx/documentation_candidate.py +939 -0
  69. sphynx/evals.py +538 -0
  70. sphynx/evaluation_protocols.py +124 -0
  71. sphynx/evaluator_bindings.py +370 -0
  72. sphynx/evidence.py +352 -0
  73. sphynx/execution_capabilities.py +109 -0
  74. sphynx/fast_search.py +285 -0
  75. sphynx/full_rag.py +341 -0
  76. sphynx/hosted/__init__.py +18 -0
  77. sphynx/hosted/benchmark_intake.py +218 -0
  78. sphynx/hosted/contracts.py +361 -0
  79. sphynx/hosted/model_admission.py +130 -0
  80. sphynx/hosted/model_artifacts.py +298 -0
  81. sphynx/hosted/model_contracts.py +63 -0
  82. sphynx/hosted/model_gcs.py +181 -0
  83. sphynx/hosted/model_history.py +84 -0
  84. sphynx/hosted/model_session.py +233 -0
  85. sphynx/hosted/public_suite.py +74 -0
  86. sphynx/hosted/registry.py +97 -0
  87. sphynx/hosted/revisions.py +126 -0
  88. sphynx/hosted/sandbox.py +105 -0
  89. sphynx/hosted/submission.py +287 -0
  90. sphynx/hosted_workflow.py +451 -0
  91. sphynx/http_transport.py +86 -0
  92. sphynx/integrated_rag.py +352 -0
  93. sphynx/integrated_workflow.py +552 -0
  94. sphynx/integration_contracts.py +474 -0
  95. sphynx/invocation.py +791 -0
  96. sphynx/invocation_candidates.py +392 -0
  97. sphynx/invocation_catalog_data.py +40 -0
  98. sphynx/learned_catalog_data.py +30 -0
  99. sphynx/learned_rerank.py +267 -0
  100. sphynx/legacy_full_rag.py +67 -0
  101. sphynx/lifecycle.py +885 -0
  102. sphynx/lifecycle_workflow.py +490 -0
  103. sphynx/metric_views.py +474 -0
  104. sphynx/metrics.py +282 -0
  105. sphynx/model_adapters.py +438 -0
  106. sphynx/model_catalog_data.py +30 -0
  107. sphynx/official_benchmarks.py +385 -0
  108. sphynx/packs.py +258 -0
  109. sphynx/pipeline.py +313 -0
  110. sphynx/pipeline_workflow.py +551 -0
  111. sphynx/pr_evaluations.py +560 -0
  112. sphynx/progress.py +89 -0
  113. sphynx/public_bundle.py +173 -0
  114. sphynx/public_eval.py +62 -0
  115. sphynx/public_rerank.py +355 -0
  116. sphynx/py.typed +0 -0
  117. sphynx/pytest_plugin.py +62 -0
  118. sphynx/question_robustness.py +437 -0
  119. sphynx/rag.py +329 -0
  120. sphynx/rag_diagnostics.py +309 -0
  121. sphynx/rag_papers/__init__.py +29 -0
  122. sphynx/rag_papers/__main__.py +45 -0
  123. sphynx/rag_papers/candidates.py +19 -0
  124. sphynx/rag_papers/contracts.py +282 -0
  125. sphynx/rag_papers/datasets.py +291 -0
  126. sphynx/rag_papers/experiments.py +116 -0
  127. sphynx/rag_papers/judges.py +308 -0
  128. sphynx/rag_papers/registry.py +33 -0
  129. sphynx/rag_papers/runner.py +176 -0
  130. sphynx/rag_papers/sable.py +135 -0
  131. sphynx/rag_papers/schemas.py +12 -0
  132. sphynx/rag_papers/scoring.py +249 -0
  133. sphynx/rag_papers/semantic.py +288 -0
  134. sphynx/rag_papers/trec.py +80 -0
  135. sphynx/recorded_scores.py +352 -0
  136. sphynx/records.py +124 -0
  137. sphynx/reflex_traces.py +193 -0
  138. sphynx/report_rendering.py +71 -0
  139. sphynx/rerank_family_contracts.py +150 -0
  140. sphynx/rerank_workflow.py +424 -0
  141. sphynx/research_candidate.py +190 -0
  142. sphynx/results.py +852 -0
  143. sphynx/retrieval.py +750 -0
  144. sphynx/retrieval_candidates.py +295 -0
  145. sphynx/retrieval_catalog_data.py +19 -0
  146. sphynx/retrieval_recordings.py +125 -0
  147. sphynx/reviewer_audit.py +266 -0
  148. sphynx/routing_policies.py +375 -0
  149. sphynx/runner.py +544 -0
  150. sphynx/sable_rerank.py +489 -0
  151. sphynx/score_reliability.py +393 -0
  152. sphynx/speech_benchmarks.py +204 -0
  153. sphynx/speech_campaigns.py +378 -0
  154. sphynx/speech_complete.py +236 -0
  155. sphynx/speech_contracts.py +225 -0
  156. sphynx/speech_evidence.py +429 -0
  157. sphynx/speech_listening.py +115 -0
  158. sphynx/speech_multilingual.py +71 -0
  159. sphynx/speech_tts.py +479 -0
  160. sphynx/sql/001_sync_v1.sql +30 -0
  161. sphynx/stage_contracts.py +199 -0
  162. sphynx/statistical_catalog_data.py +53 -0
  163. sphynx/statistical_rerank.py +232 -0
  164. sphynx/sync.py +797 -0
  165. sphynx/workbench.py +378 -0
  166. sphynx/workbench_catalog_data.py +13 -0
  167. sphynx/workbench_tracks.py +78 -0
  168. sphynx/workflow_async.py +197 -0
  169. sphynx/workflow_contracts.py +594 -0
  170. sphynx/workflow_decisions.py +211 -0
  171. sphynx/workflow_defaults.py +16 -0
  172. sphynx/workflow_extensions.py +212 -0
  173. sphynx/workflow_registry.py +513 -0
  174. sphynx/workflows.py +792 -0
@@ -0,0 +1,295 @@
1
+ """Portable seven-arm retrieval candidates with explicit signal provenance.
2
+
3
+ The core is dependency free. Model backends are explicit caller-supplied objects;
4
+ recorded signals never become fresh inference by running the assembly code.
5
+ Reference labels are not accepted by candidate or signal contracts.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import math
12
+ from collections import defaultdict
13
+ from collections.abc import Callable, Mapping, Sequence
14
+ from dataclasses import dataclass
15
+ from pathlib import Path
16
+ from types import MappingProxyType
17
+ from typing import Any
18
+
19
+ from .contracts import ContractError, canonical_json
20
+ from . import customer_rerank as _bm25_module
21
+ from .integration_contracts import CandidateConfiguration, value_digest, _freeze
22
+ from .retrieval import RETRIEVAL_PACK_ID, RETRIEVAL_INTERFACE_VERSION, RETRIEVAL_REQUEST_SCHEMA, RETRIEVAL_RESPONSE_SCHEMA, _response, _policy
23
+
24
+ METHODS = ("bm25-only", "dense-a-only", "dense-b-only", "hybrid-a-rrf",
25
+ "hybrid-b-rrf", "hybrid-a-rrf-minilm", "hybrid-b-rrf-minilm")
26
+ MODEL_PROFILES = MappingProxyType({key: MappingProxyType(value) for key, value in {
27
+ "embedding-a": {"checkpoint": "Snowflake/snowflake-arctic-embed-m-v2.0",
28
+ "revision": "95c2741480856aa9666782eb4afe11959938017f", "query_prompt": "query: "},
29
+ "embedding-b": {"checkpoint": "Snowflake/snowflake-arctic-embed-m-v1.5",
30
+ "revision": "e58a8f756156a1293d763f17e3aae643474e9b8a", "query_prompt": "Represent this sentence for searching relevant passages: "},
31
+ "reranker": {"checkpoint": "cross-encoder/ms-marco-MiniLM-L-6-v2",
32
+ "revision": "233902d25c440f23af6f7d6e94d2946bac0bee0a"},
33
+ }.items()})
34
+ IMPLEMENTATION_DIGEST = "sha256:" + hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
35
+ BM25_IMPLEMENTATION_DIGEST = "sha256:" + hashlib.sha256(Path(_bm25_module.__file__).read_bytes()).hexdigest()
36
+
37
+
38
+ def reciprocal_rank_fusion(branches: Sequence[Sequence[str]], *, k: int = 60) -> list[str]:
39
+ if type(k) is not int or k <= 0:
40
+ raise ContractError("RRF k must be a positive integer")
41
+ scores: dict[str, float] = defaultdict(float)
42
+ for branch in branches:
43
+ if len(branch) != len(set(branch)) or any(type(cid) is not str or not cid for cid in branch):
44
+ raise ContractError("RRF branches require unique nonempty chunk IDs")
45
+ for rank, cid in enumerate(branch, 1):
46
+ scores[cid] += 1.0 / (k + rank)
47
+ return sorted(scores, key=lambda cid: (-scores[cid], cid))
48
+
49
+
50
+ def select_whole_chunk_context(ranked_ids: Sequence[str], corpus: Mapping[str, Mapping], *,
51
+ max_context_bytes: int = 16000, top_k: int = 5) -> list[str]:
52
+ if type(max_context_bytes) is not int or max_context_bytes < 1 or type(top_k) is not int or top_k < 1:
53
+ raise ContractError("positive integer context limits required")
54
+ chosen: list[str] = []
55
+ used = 0
56
+ for cid in ranked_ids:
57
+ added = len(corpus[cid]["text"].encode("utf-8")) + (2 if chosen else 0)
58
+ if used + added <= max_context_bytes:
59
+ chosen.append(cid)
60
+ used += added
61
+ if len(chosen) == top_k:
62
+ break
63
+ return chosen
64
+
65
+
66
+ def _copy(value: Any) -> Any:
67
+ def thaw(item):
68
+ if isinstance(item, Mapping):
69
+ return {key: thaw(child) for key, child in item.items()}
70
+ if isinstance(item, (tuple, list)):
71
+ return [thaw(child) for child in item]
72
+ return item
73
+ return json.loads(canonical_json(thaw(value)))
74
+
75
+
76
+ def _request(request: Mapping[str, Any]) -> dict:
77
+ expected = {"schema_version", "pack_id", "interface_version", "query_id", "query", "customer",
78
+ "conversation_context", "corpus_digest", "execution_policy", "corpus", "request_sha256"}
79
+ if not isinstance(request, Mapping) or set(request) != expected:
80
+ raise ContractError("retrieval request must contain only the declared label-free fields")
81
+ copied = _copy(dict(request))
82
+ for name in ("query_id", "query", "customer", "corpus_digest"):
83
+ if type(copied[name]) is not str or not copied[name].strip() or "\x00" in copied[name]:
84
+ raise ContractError("retrieval request identities and question must be nonempty text")
85
+ if (copied["schema_version"] != RETRIEVAL_REQUEST_SCHEMA or copied["interface_version"] != RETRIEVAL_INTERFACE_VERSION
86
+ or copied["pack_id"] != RETRIEVAL_PACK_ID):
87
+ raise ContractError("retrieval interface mismatch")
88
+ if copied["request_sha256"] != value_digest({key: value for key, value in copied.items() if key != "request_sha256"}):
89
+ raise ContractError("retrieval request digest mismatch")
90
+ corpus = copied["corpus"]
91
+ if not isinstance(corpus, list) or not corpus:
92
+ raise ContractError("customer-scoped corpus is required")
93
+ fields = {"chunk_id", "document_id", "customer", "text", "text_sha256", "source_class", "source_uri"}
94
+ if any(not isinstance(chunk, dict) or set(chunk) != fields or
95
+ any(type(value) is not str or not value.strip() or "\x00" in value for value in chunk.values())
96
+ for chunk in corpus):
97
+ raise ContractError("corpus chunks must contain only declared label-free fields")
98
+ ids = [chunk.get("chunk_id") for chunk in corpus]
99
+ if any(type(cid) is not str or not cid for cid in ids) or len(ids) != len(set(ids)):
100
+ raise ContractError("corpus chunk IDs must be unique")
101
+ for chunk in corpus:
102
+ if chunk.get("customer") != copied["customer"]:
103
+ raise ContractError("cross-customer corpus is forbidden")
104
+ text = chunk.get("text")
105
+ if type(text) is not str or chunk.get("text_sha256", "").removeprefix("sha256:") != hashlib.sha256(text.encode()).hexdigest():
106
+ raise ContractError("corpus text hash mismatch")
107
+ policy = copied["execution_policy"]
108
+ if not isinstance(policy, dict) or canonical_json(policy) != canonical_json(_policy(policy.get("max_context_bytes"))):
109
+ raise ContractError("seven-arm execution policy differs from the declared policy")
110
+ return copied
111
+
112
+
113
+ def _rank_ids(raw: Sequence[str], request: Mapping) -> list[str]:
114
+ if (not isinstance(raw, (list, tuple)) or any(type(cid) is not str for cid in raw)
115
+ or len(raw) != len(set(raw))):
116
+ raise ContractError("dense backend must return unique ranked IDs")
117
+ allowed = {chunk["chunk_id"] for chunk in request["corpus"]}
118
+ if len(raw) != min(50, len(allowed)) or any(cid not in allowed for cid in raw):
119
+ raise ContractError("dense backend requires complete top-50 customer-scoped output")
120
+ return list(raw)
121
+
122
+
123
+ def _rank_scores(scores: Mapping, expected_ids: Sequence[str]) -> list[str]:
124
+ if not isinstance(scores, Mapping) or set(scores) != set(expected_ids):
125
+ raise ContractError("reranker must score exactly the fused candidate pool")
126
+ for value in scores.values():
127
+ if type(value) not in (int, float) or not math.isfinite(value):
128
+ raise ContractError("reranker scores must be finite numbers")
129
+ return sorted(expected_ids, key=lambda cid: (-scores[cid], cid))
130
+
131
+
132
+ @dataclass(frozen=True)
133
+ class RetrievalCandidate:
134
+ _identity_json: str
135
+ adapter: Callable[[dict], dict]
136
+ execution_mode: str
137
+
138
+ @property
139
+ def identity(self) -> dict:
140
+ return json.loads(self._identity_json)
141
+
142
+ def run(self, dataset_path, output_dir, **kwargs):
143
+ """Use existing SDK capture policy; replay has no inference latency."""
144
+ from .retrieval import capture_retrieval, load_retrieval_pack, build_retrieval_request, run_retrieval
145
+ if self.execution_mode == "recorded":
146
+ pack = load_retrieval_pack(dataset_path, expected_sha256=kwargs.get("expected_sha256"))
147
+ responses = [{"query_id": case["query_id"], "response": self.adapter(build_retrieval_request(
148
+ pack, case, max_context_bytes=kwargs.get("max_context_bytes", 16000)))} for case in pack.cases]
149
+ return capture_retrieval(self.identity, dataset_path, responses, output_dir, **kwargs)
150
+ return run_retrieval(self.identity, self.adapter, dataset_path, output_dir, **kwargs)
151
+
152
+
153
+ class RecordedHybridSignals:
154
+ """Read only exact request-bound dense orders and previously inferred logits.
155
+
156
+ This archive contains no reference labels or complete candidate responses.
157
+ Fusion, ranking, context assembly and grading are executed again, while the
158
+ upstream neural signals retain their original recorded provenance.
159
+ """
160
+ def __init__(self, artifact: Mapping, *, expected_digest: str):
161
+ raw = _copy(dict(artifact))
162
+ if value_digest(raw) != expected_digest:
163
+ raise ContractError("recorded retrieval signal digest mismatch")
164
+ if set(raw) != {"schema_version", "source", "models", "requests"} or raw["schema_version"] != "sphynx.retrieval.recorded-signals.v1":
165
+ raise ContractError("invalid recorded signal archive")
166
+ if raw["models"] != _copy(MODEL_PROFILES):
167
+ raise ContractError("recorded model identities mismatch")
168
+ source_fields = {"dataset_digest", "corpus_digest", "cohort_digest", "experiment_digest", "generation_receipt_digest",
169
+ "source_files", "model_snapshot_digests", "provenance", "limitations"}
170
+ if not isinstance(raw["source"], dict) or set(raw["source"]) - source_fields:
171
+ raise ContractError("unknown recorded provenance fields")
172
+ self._identity_json = canonical_json({"mode": "recorded", "artifact_digest": expected_digest,
173
+ "models": _copy(raw["models"]), "source": _copy(raw["source"])})
174
+ rows = raw["requests"]
175
+ if not isinstance(rows, list) or not rows:
176
+ raise ContractError("recorded request inventory is empty")
177
+ self._rows = {}
178
+ for row in rows:
179
+ if set(row) != {"query_id", "request_sha256", "corpus_digest", "dense", "reranker"}:
180
+ raise ContractError("unknown recorded signal fields; reference labels are not permitted")
181
+ digest = row["request_sha256"]
182
+ if digest in self._rows or type(digest) is not str:
183
+ raise ContractError("duplicate recorded request identity")
184
+ self._rows[digest] = _freeze(row)
185
+ self._rows = MappingProxyType(self._rows)
186
+
187
+ @property
188
+ def identity(self):
189
+ return json.loads(self._identity_json)
190
+
191
+ def _row(self, request):
192
+ row = self._rows.get(request["request_sha256"])
193
+ if row is None or any(row[field] != request[field] for field in ("query_id", "corpus_digest")):
194
+ raise ContractError("recorded signals do not bind this exact request")
195
+ return row
196
+
197
+ def dense_rank(self, request, profile):
198
+ row = self._row(request)
199
+ if profile not in row["dense"]:
200
+ raise ContractError("recorded dense profile missing")
201
+ return list(row["dense"][profile])
202
+
203
+ def rerank_scores(self, request, profile, ids):
204
+ row = self._row(request)
205
+ if profile not in row["reranker"]:
206
+ raise ContractError("recorded reranker profile missing")
207
+ scores = row["reranker"][profile]
208
+ if set(scores) != set(ids):
209
+ raise ContractError("recorded fused pool differs; refusing partial or changed score replay")
210
+ return dict(scores)
211
+
212
+
213
+ def make_retrieval_candidate(method: str, *, configuration: CandidateConfiguration, backend=None) -> RetrievalCandidate:
214
+ """Compose a portable algorithm; constructing it does not load any model."""
215
+ if method not in METHODS or not isinstance(configuration, CandidateConfiguration):
216
+ raise ContractError("unsupported retrieval method/configuration")
217
+ if configuration.interface_version != RETRIEVAL_INTERFACE_VERSION or configuration.source_digest != IMPLEMENTATION_DIGEST:
218
+ raise ContractError("retrieval configuration source/interface mismatch")
219
+ if configuration.execution_mode not in ("trusted_local", "recorded") or configuration.credential_refs:
220
+ raise ContractError("retrieval families accept explicit local or recorded backends, without credentials")
221
+ params = _copy(dict(configuration.parameters))
222
+ if set(params) != {"method", "rrf_k", "max_context_bytes", "models", "backend_identity"} or params["method"] != method:
223
+ raise ContractError("retrieval configuration must pin method, policy, models and backend identity")
224
+ if type(params["rrf_k"]) is not int or params["rrf_k"] <= 0 or type(params["max_context_bytes"]) is not int or params["max_context_bytes"] <= 0:
225
+ raise ContractError("positive integer retrieval limits required")
226
+ required = {} if method == "bm25-only" else {"embedding-a" if "-a-" in method else "embedding-b":
227
+ dict(MODEL_PROFILES["embedding-a" if "-a-" in method else "embedding-b"])}
228
+ if method.endswith("-minilm"):
229
+ required["reranker"] = dict(MODEL_PROFILES["reranker"])
230
+ if params["models"] != required:
231
+ raise ContractError("method model identities must match original pinned profiles")
232
+ if method == "bm25-only":
233
+ if backend is not None or configuration.execution_mode != "trusted_local" or params["backend_identity"] != {"mode": "fresh_bm25"}:
234
+ raise ContractError("BM25 is fresh deterministic execution and needs no model backend")
235
+ elif backend is None or params["backend_identity"] != _copy(dict(backend.identity)):
236
+ raise ContractError("backend identity does not match frozen configuration")
237
+ elif backend.identity.get("mode") != configuration.execution_mode:
238
+ raise ContractError("backend execution mode mismatch")
239
+ profile = "embedding-a" if "-a-" in method else "embedding-b"
240
+
241
+ def adapter(request):
242
+ if backend is not None and _copy(dict(backend.identity)) != params["backend_identity"]:
243
+ raise ContractError("backend identity changed after configuration was frozen")
244
+ checked = _request(request)
245
+ if checked["execution_policy"]["max_context_bytes"] != params["max_context_bytes"]:
246
+ raise ContractError("request context policy differs from candidate configuration")
247
+ corpus = {chunk["chunk_id"]: chunk for chunk in checked["corpus"]}
248
+ bm25 = None
249
+ if method == "bm25-only" or method.startswith("hybrid-"):
250
+ bm25 = [chunk["chunk_id"] for _, chunk in _bm25_module._rank(checked["query"], checked["corpus"])[:50]]
251
+ if method == "bm25-only":
252
+ pool = fused = bm25
253
+ else:
254
+ dense = _rank_ids(backend.dense_rank(checked, profile), checked)
255
+ if method.startswith("dense-"):
256
+ pool = fused = dense
257
+ else:
258
+ branches = [bm25[:25], dense[:25]]
259
+ pool = list(dict.fromkeys(branches[0] + branches[1]))
260
+ fused = reciprocal_rank_fusion(branches, k=params["rrf_k"])
261
+ ranked = _rank_scores(backend.rerank_scores(checked, profile, fused), fused) if method.endswith("-minilm") else list(fused)
262
+ context = select_whole_chunk_context(ranked, corpus, max_context_bytes=params["max_context_bytes"])
263
+ result = {"schema_version": RETRIEVAL_RESPONSE_SCHEMA,
264
+ **{field: checked[field] for field in ("query_id", "request_sha256", "corpus_digest")},
265
+ "stages": {"candidate": list(pool), "fused": list(fused), "reranked": ranked, "context": context}}
266
+ return _response(result, checked)
267
+
268
+ identity = {"name": method, "version": configuration.configuration_id,
269
+ "interface_version": RETRIEVAL_INTERFACE_VERSION,
270
+ "architecture": {"embedding": required or "none", "index": "exact_customer_scoped_top50.v1",
271
+ "fusion": "equal_rrf_k=" + str(params["rrf_k"]) if method.startswith("hybrid-") else "none",
272
+ "reranker": required.get("reranker", "none"), "chunking": "pinned_source_chunks",
273
+ "context_selection": {"top_k": 5, "max_context_bytes": params["max_context_bytes"], "separator": "\n\n"},
274
+ "code_revision": IMPLEMENTATION_DIGEST, "configuration_digest": configuration.digest,
275
+ "bm25_implementation_digest": BM25_IMPLEMENTATION_DIGEST,
276
+ "execution_mode": configuration.execution_mode, "backend_identity": params["backend_identity"]}}
277
+ return RetrievalCandidate(canonical_json(identity), adapter, configuration.execution_mode)
278
+
279
+
280
+ def retrieval_configuration(method: str, *, backend=None, rrf_k: int = 60, max_context_bytes: int = 16000,
281
+ configuration_id: str = "original-seven-arms-v1") -> CandidateConfiguration:
282
+ if method not in METHODS:
283
+ raise ContractError("unsupported retrieval method")
284
+ if method != "bm25-only" and backend is None:
285
+ raise ContractError("dense/hybrid retrieval requires an explicit pinned backend")
286
+ models = {}
287
+ if method != "bm25-only":
288
+ profile = "embedding-a" if "-a-" in method else "embedding-b"
289
+ models[profile] = dict(MODEL_PROFILES[profile])
290
+ if method.endswith("-minilm"):
291
+ models["reranker"] = dict(MODEL_PROFILES["reranker"])
292
+ return CandidateConfiguration(configuration_id=configuration_id, interface_version=RETRIEVAL_INTERFACE_VERSION,
293
+ source_digest=IMPLEMENTATION_DIGEST, execution_mode="trusted_local" if method == "bm25-only" else backend.identity["mode"],
294
+ parameters={"method": method, "rrf_k": rrf_k, "max_context_bytes": max_context_bytes,
295
+ "models": models, "backend_identity": {"mode": "fresh_bm25"} if method == "bm25-only" else _copy(dict(backend.identity))})
@@ -0,0 +1,19 @@
1
+ """Content-free seven-arm retrieval discovery; optional backends are not loaded."""
2
+ from .catalog import CandidateDescriptor
3
+
4
+ _SOURCE = 'sha256:270118e6c0606a5f54942e6a940e11b3150aba7b71b689f0fb0670df50cf908c'
5
+ _METHODS = ("bm25-only", "dense-a-only", "dense-b-only", "hybrid-a-rrf",
6
+ "hybrid-b-rrf", "hybrid-a-rrf-minilm", "hybrid-b-rrf-minilm")
7
+ CANDIDATES = tuple(CandidateDescriptor(
8
+ candidate_id="retrieval-" + method + "-v1", track="retrieval",
9
+ interface_version="sphynx.rag.retrieve.v1", source_digest=_SOURCE,
10
+ supported_dataset_ids=("sable.hybrid100.retrieve.v1",),
11
+ execution_modes=("trusted_local",) if method == "bm25-only" else ("recorded", "trusted_local"),
12
+ implementation_status="available",
13
+ reason=("Dependency-free fresh BM25 available." if method == "bm25-only" else
14
+ "Assembly is available; recorded neural signals are verified. Fresh neural execution requires an explicit pinned offline backend and is not inferred from replay."),
15
+ local_prerequisites=("authorized exact customer-filtered corpus and dataset",) if method == "bm25-only" else
16
+ ("authorized exact corpus and dataset", "exact request-bound recorded signal archive or pinned caller-provided offline model backend",),
17
+ dependencies=(), model_identity_required=method != "bm25-only",
18
+ supported_python=("3.11", "3.12", "3.13", "3.14"),
19
+ ) for method in _METHODS)
@@ -0,0 +1,125 @@
1
+ """Explicit owner-side migration of sealed historical hybrid neural signals.
2
+
3
+ Reads local input and original receipts; no model, provider or network access.
4
+ The output contains request bindings, dense order and raw reranker scores only.
5
+ It must stay outside a distributable wheel or candidate source PR.
6
+ """
7
+ from __future__ import annotations
8
+ import hashlib
9
+ import json
10
+ from pathlib import Path
11
+
12
+ from .contracts import ContractError, canonical_json
13
+ from .integration_contracts import require_digest, value_digest
14
+ from .retrieval import build_retrieval_request, load_retrieval_pack
15
+ from .retrieval_candidates import METHODS, MODEL_PROFILES, RecordedHybridSignals
16
+
17
+
18
+ def _digest(path: Path) -> str:
19
+ h = hashlib.sha256()
20
+ with path.open("rb") as stream:
21
+ for block in iter(lambda: stream.read(1048576), b""):
22
+ h.update(block)
23
+ return "sha256:" + h.hexdigest()
24
+
25
+
26
+ def import_hybrid_recordings(dataset_path, source_directory, output_path, *,
27
+ expected_dataset_digest, expected_experiment_digest,
28
+ expected_generation_receipt_digest=None):
29
+ """Freeze the complete source-bound signal archive without altering originals.
30
+
31
+ The initial historical run was sealed after execution, not at its start.
32
+ Preserve that limitation. No missing query vectors are synthesized.
33
+ """
34
+ root = Path(source_directory).resolve(strict=True)
35
+ dataset = Path(dataset_path).resolve(strict=True)
36
+ require_digest(expected_dataset_digest, "expected_dataset_digest")
37
+ require_digest(expected_experiment_digest, "expected_experiment_digest")
38
+ if _digest(root / "experiment.json") != expected_experiment_digest:
39
+ raise ContractError("historical experiment digest mismatch")
40
+ pack = load_retrieval_pack(dataset, expected_sha256=expected_dataset_digest)
41
+ experiment = json.loads((root / "experiment.json").read_bytes())
42
+ if experiment.get("schema_version") != "sphynx.hybrid.offline.experiment.v1" or experiment.get("arms") != list(METHODS):
43
+ raise ContractError("unsupported historical seven-arm generation")
44
+ if experiment["policy"]["dataset_sha256"] != expected_dataset_digest:
45
+ raise ContractError("historical source binds another dataset")
46
+ if experiment.get("case_count") != len(pack.cases) or experiment.get("chunk_count") != len(pack.corpus):
47
+ raise ContractError("historical generation inventory differs from the dataset")
48
+ receipt = None
49
+ hashes = experiment.get("artifact_hashes")
50
+ if hashes is None:
51
+ require_digest(expected_generation_receipt_digest, "expected_generation_receipt_digest")
52
+ path = root / "generation-receipt.json"
53
+ if _digest(path) != expected_generation_receipt_digest:
54
+ raise ContractError("historical generation receipt digest mismatch")
55
+ receipt = json.loads(path.read_bytes())
56
+ if (receipt.get("schema_version") != "sphynx.hybrid.generation-receipt.v1"
57
+ or receipt.get("experiment_sha256") != expected_experiment_digest
58
+ or receipt.get("post_generation_dataset_sha256") != expected_dataset_digest
59
+ or receipt.get("generation_tool_sha256") != experiment["policy"]["code_sha256"]):
60
+ raise ContractError("historical receipt does not bind source and dataset")
61
+ hashes = receipt.get("artifact_hashes")
62
+ if not isinstance(hashes, dict) or not hashes:
63
+ raise ContractError("historical source inventory is missing")
64
+ required = {method + "-stages.json" for method in METHODS} | {"reranker-scores.json"}
65
+ if not required <= set(hashes):
66
+ raise ContractError("historical source has incomplete stage/score coverage")
67
+ for name, expected in hashes.items():
68
+ require_digest(expected, "historical artifact digest")
69
+ if not isinstance(name, str) or Path(name).name != name or name in ("experiment.json", "generation-receipt.json"):
70
+ raise ContractError("historical archive path is invalid")
71
+ path = (root / name).resolve(strict=True)
72
+ if path.parent != root or _digest(path) != expected:
73
+ raise ContractError("historical archive artifact changed")
74
+ generation_name = receipt["generation_source_name"] if receipt else experiment["policy"].get("generation_source_name")
75
+ if generation_name not in hashes or hashes[generation_name] != experiment["policy"]["code_sha256"]:
76
+ raise ContractError("original generation source is not bound")
77
+ for profile, spec in MODEL_PROFILES.items():
78
+ recorded = experiment["models"].get(profile)
79
+ if not isinstance(recorded, dict) or any(recorded.get(k) != v for k, v in spec.items()):
80
+ raise ContractError("historical model profile mismatch")
81
+ stage_rows = {}
82
+ query_ids = {case["query_id"] for case in pack.cases}
83
+ for method in METHODS:
84
+ value = json.loads((root / (method + "-stages.json")).read_bytes())
85
+ if set(value) != {"arm_id", "cases"} or value["arm_id"] != method or set(value["cases"]) != query_ids:
86
+ raise ContractError("historical arm has incomplete or foreign cases")
87
+ stage_rows[method] = value["cases"]
88
+ raw_scores = json.loads((root / "reranker-scores.json").read_bytes())
89
+ scores = {}
90
+ for method in ("hybrid-a-rrf-minilm", "hybrid-b-rrf-minilm"):
91
+ rows = raw_scores.get(method)
92
+ if not isinstance(rows, list) or len(rows) != len(query_ids):
93
+ raise ContractError("historical model score inventory is incomplete")
94
+ scores[method] = {row["query_id"]: row["scores"] for row in rows if set(row) == {"query_id", "scores"}}
95
+ if len(scores[method]) != len(rows) or set(scores[method]) != query_ids:
96
+ raise ContractError("historical model scores contain duplicates or unknown cases")
97
+ requests = []
98
+ for case in pack.cases:
99
+ request = build_retrieval_request(pack, case, max_context_bytes=experiment["policy"]["max_context_bytes"])
100
+ rows = {"query_id": request["query_id"], "request_sha256": request["request_sha256"],
101
+ "corpus_digest": request["corpus_digest"],
102
+ "dense": {profile: stage_rows[method][case["query_id"]]["candidate"]
103
+ for profile, method in (("embedding-a", "dense-a-only"), ("embedding-b", "dense-b-only"))},
104
+ "reranker": {profile: scores[method][case["query_id"]]
105
+ for profile, method in (("embedding-a", "hybrid-a-rrf-minilm"), ("embedding-b", "hybrid-b-rrf-minilm"))}}
106
+ requests.append(rows)
107
+ artifact = {"schema_version": "sphynx.retrieval.recorded-signals.v1",
108
+ "models": {profile: dict(value) for profile, value in MODEL_PROFILES.items()},
109
+ "requests": requests,
110
+ "source": {"dataset_digest": pack.dataset_digest, "corpus_digest": pack.corpus_digest,
111
+ "cohort_digest": pack.cohort_digest, "experiment_digest": expected_experiment_digest,
112
+ "generation_receipt_digest": expected_generation_receipt_digest,
113
+ "source_files": hashes,
114
+ "model_snapshot_digests": {key: value["snapshot_digest"] for key, value in experiment["models"].items()},
115
+ "provenance": receipt.get("provenance") if receipt else "generation_bound_artifacts",
116
+ "limitations": [receipt["dataset_attestation_scope"], receipt["bm25_dependency_attestation_scope"]] if receipt else []}}
117
+ digest = value_digest(artifact)
118
+ RecordedHybridSignals(artifact, expected_digest=digest)
119
+ target = Path(output_path)
120
+ target.parent.mkdir(parents=True, exist_ok=True)
121
+ with target.open("x", encoding="utf-8") as stream:
122
+ stream.write(canonical_json(artifact) + "\n")
123
+ return {"path": str(target), "artifact_digest": digest, "file_digest": _digest(target),
124
+ "case_count": len(requests), "execution_mode": "recorded", "model_inference": False,
125
+ "source_provenance": artifact["source"]["provenance"], "limitations": artifact["source"]["limitations"]}