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.
- sable_sphynx-0.7.0.dev0.dist-info/METADATA +152 -0
- sable_sphynx-0.7.0.dev0.dist-info/RECORD +174 -0
- sable_sphynx-0.7.0.dev0.dist-info/WHEEL +5 -0
- sable_sphynx-0.7.0.dev0.dist-info/top_level.txt +1 -0
- sphynx/__init__.py +469 -0
- sphynx/__init__.pyi +376 -0
- sphynx/_analytical_client.py +77 -0
- sphynx/_analytical_values.py +252 -0
- sphynx/_audit_client.py +173 -0
- sphynx/_comparison_adapters.py +113 -0
- sphynx/_componentized_full_rag.py +34 -0
- sphynx/_composed_pipeline.py +109 -0
- sphynx/_full_rag_grading.py +218 -0
- sphynx/_full_rag_trace.py +221 -0
- sphynx/_local_eval.py +268 -0
- sphynx/_pipeline_core.py +243 -0
- sphynx/_pr_client.py +237 -0
- sphynx/_rag_diagnostics_calibration.py +285 -0
- sphynx/_rag_diagnostics_catalog.py +39 -0
- sphynx/_rag_diagnostics_common.py +26 -0
- sphynx/_rag_diagnostics_contracts.py +186 -0
- sphynx/_rag_diagnostics_engine.py +243 -0
- sphynx/_rag_diagnostics_importers.py +242 -0
- sphynx/_rag_diagnostics_ranking.py +81 -0
- sphynx/_rag_diagnostics_semantic.py +325 -0
- sphynx/_rag_ranking.py +149 -0
- sphynx/_speech_benchmark_scoring.py +140 -0
- sphynx/_speech_benchmark_sources.py +275 -0
- sphynx/_speech_complete_scoring.py +218 -0
- sphynx/_speech_complete_source.py +118 -0
- sphynx/_speech_kido.py +294 -0
- sphynx/_speech_scoring.py +77 -0
- sphynx/_track_capture.py +213 -0
- sphynx/analytical_views.py +515 -0
- sphynx/assessment_contracts.py +378 -0
- sphynx/assessment_selection.py +57 -0
- sphynx/audit_access.py +245 -0
- sphynx/audit_contracts.py +204 -0
- sphynx/audit_requests.py +180 -0
- sphynx/authoring.py +188 -0
- sphynx/benchmarks.py +568 -0
- sphynx/candidates.py +62 -0
- sphynx/catalog.py +272 -0
- sphynx/chunking.py +372 -0
- sphynx/clickhouse_reader.py +169 -0
- sphynx/client.py +486 -0
- sphynx/comparison_core.py +149 -0
- sphynx/comparison_views.py +223 -0
- sphynx/components.py +1661 -0
- sphynx/contracts.py +514 -0
- sphynx/customer_rerank.py +299 -0
- sphynx/customer_retrieval.py +146 -0
- sphynx/data/benchmark-releases.v1.json +164 -0
- sphynx/data/public-benchmark-release.v1.json +354 -0
- sphynx/data/rag-diagnostics-synthetic.v1.json +6982 -0
- sphynx/data/rag-paper-references.v1.json +176 -0
- sphynx/data/rag-paper-schemas.v1.json +1069 -0
- sphynx/data/rag-rerank-v1.json +507 -0
- sphynx/dataset_catalog_data.py +1214 -0
- sphynx/dataset_readiness.py +138 -0
- sphynx/dataset_v3.py +999 -0
- sphynx/demo.py +130 -0
- sphynx/demo_candidates/__init__.py +1 -0
- sphynx/demo_candidates/dense.py +11 -0
- sphynx/demo_candidates/lexical.py +21 -0
- sphynx/diagnostics.py +207 -0
- sphynx/documentation.py +737 -0
- sphynx/documentation_candidate.py +939 -0
- sphynx/evals.py +538 -0
- sphynx/evaluation_protocols.py +124 -0
- sphynx/evaluator_bindings.py +370 -0
- sphynx/evidence.py +352 -0
- sphynx/execution_capabilities.py +109 -0
- sphynx/fast_search.py +285 -0
- sphynx/full_rag.py +341 -0
- sphynx/hosted/__init__.py +18 -0
- sphynx/hosted/benchmark_intake.py +218 -0
- sphynx/hosted/contracts.py +361 -0
- sphynx/hosted/model_admission.py +130 -0
- sphynx/hosted/model_artifacts.py +298 -0
- sphynx/hosted/model_contracts.py +63 -0
- sphynx/hosted/model_gcs.py +181 -0
- sphynx/hosted/model_history.py +84 -0
- sphynx/hosted/model_session.py +233 -0
- sphynx/hosted/public_suite.py +74 -0
- sphynx/hosted/registry.py +97 -0
- sphynx/hosted/revisions.py +126 -0
- sphynx/hosted/sandbox.py +105 -0
- sphynx/hosted/submission.py +287 -0
- sphynx/hosted_workflow.py +451 -0
- sphynx/http_transport.py +86 -0
- sphynx/integrated_rag.py +352 -0
- sphynx/integrated_workflow.py +552 -0
- sphynx/integration_contracts.py +474 -0
- sphynx/invocation.py +791 -0
- sphynx/invocation_candidates.py +392 -0
- sphynx/invocation_catalog_data.py +40 -0
- sphynx/learned_catalog_data.py +30 -0
- sphynx/learned_rerank.py +267 -0
- sphynx/legacy_full_rag.py +67 -0
- sphynx/lifecycle.py +885 -0
- sphynx/lifecycle_workflow.py +490 -0
- sphynx/metric_views.py +474 -0
- sphynx/metrics.py +282 -0
- sphynx/model_adapters.py +438 -0
- sphynx/model_catalog_data.py +30 -0
- sphynx/official_benchmarks.py +385 -0
- sphynx/packs.py +258 -0
- sphynx/pipeline.py +313 -0
- sphynx/pipeline_workflow.py +551 -0
- sphynx/pr_evaluations.py +560 -0
- sphynx/progress.py +89 -0
- sphynx/public_bundle.py +173 -0
- sphynx/public_eval.py +62 -0
- sphynx/public_rerank.py +355 -0
- sphynx/py.typed +0 -0
- sphynx/pytest_plugin.py +62 -0
- sphynx/question_robustness.py +437 -0
- sphynx/rag.py +329 -0
- sphynx/rag_diagnostics.py +309 -0
- sphynx/rag_papers/__init__.py +29 -0
- sphynx/rag_papers/__main__.py +45 -0
- sphynx/rag_papers/candidates.py +19 -0
- sphynx/rag_papers/contracts.py +282 -0
- sphynx/rag_papers/datasets.py +291 -0
- sphynx/rag_papers/experiments.py +116 -0
- sphynx/rag_papers/judges.py +308 -0
- sphynx/rag_papers/registry.py +33 -0
- sphynx/rag_papers/runner.py +176 -0
- sphynx/rag_papers/sable.py +135 -0
- sphynx/rag_papers/schemas.py +12 -0
- sphynx/rag_papers/scoring.py +249 -0
- sphynx/rag_papers/semantic.py +288 -0
- sphynx/rag_papers/trec.py +80 -0
- sphynx/recorded_scores.py +352 -0
- sphynx/records.py +124 -0
- sphynx/reflex_traces.py +193 -0
- sphynx/report_rendering.py +71 -0
- sphynx/rerank_family_contracts.py +150 -0
- sphynx/rerank_workflow.py +424 -0
- sphynx/research_candidate.py +190 -0
- sphynx/results.py +852 -0
- sphynx/retrieval.py +750 -0
- sphynx/retrieval_candidates.py +295 -0
- sphynx/retrieval_catalog_data.py +19 -0
- sphynx/retrieval_recordings.py +125 -0
- sphynx/reviewer_audit.py +266 -0
- sphynx/routing_policies.py +375 -0
- sphynx/runner.py +544 -0
- sphynx/sable_rerank.py +489 -0
- sphynx/score_reliability.py +393 -0
- sphynx/speech_benchmarks.py +204 -0
- sphynx/speech_campaigns.py +378 -0
- sphynx/speech_complete.py +236 -0
- sphynx/speech_contracts.py +225 -0
- sphynx/speech_evidence.py +429 -0
- sphynx/speech_listening.py +115 -0
- sphynx/speech_multilingual.py +71 -0
- sphynx/speech_tts.py +479 -0
- sphynx/sql/001_sync_v1.sql +30 -0
- sphynx/stage_contracts.py +199 -0
- sphynx/statistical_catalog_data.py +53 -0
- sphynx/statistical_rerank.py +232 -0
- sphynx/sync.py +797 -0
- sphynx/workbench.py +378 -0
- sphynx/workbench_catalog_data.py +13 -0
- sphynx/workbench_tracks.py +78 -0
- sphynx/workflow_async.py +197 -0
- sphynx/workflow_contracts.py +594 -0
- sphynx/workflow_decisions.py +211 -0
- sphynx/workflow_defaults.py +16 -0
- sphynx/workflow_extensions.py +212 -0
- sphynx/workflow_registry.py +513 -0
- 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"]}
|