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,424 @@
1
+ """Native adapter for the installed illustrative frozen-pool reranking suite.
2
+
3
+ RAG schemas, fixed case membership, local policy and metric layouts belong in
4
+ this adapter. Shared orchestration uses WorkflowBackend and native references.
5
+ The adapter preserves local_unverified evidence; admission is not a sandbox or
6
+ verification of candidate source bytes.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from dataclasses import asdict
12
+ from importlib import resources
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ from .assessment_contracts import ASSESSMENT_VIEW_SCHEMA, Assessment, CaseView
17
+ from .contracts import (
18
+ PACK_ID, REQUEST_SCHEMA_VERSION, CandidateSpec, CaseReferences, EvidenceGroup,
19
+ ReferenceCandidate, build_candidate_request, canonical_json, validate_candidate_request,
20
+ )
21
+ from .evals import ResolvedInvocation, require_legacy_rerank, resolve_eval
22
+ from .evidence import RunRef, read_run_manifest, sha256_bytes
23
+ from .metrics import GRADER_RELEASE, METRIC_RELEASE, metric_definitions
24
+ from .packs import check_candidate, get_pack
25
+ from .rag import FIXTURE_SHA256, load_rag_rerank_pack, validate_dataset_v2
26
+ from .results import Comparison, EvaluationRef, ResultError, Scorecard
27
+ from .results import compare as native_compare
28
+ from .results import grade as native_grade
29
+ from .results import report as native_report
30
+ from .runner import run as native_run
31
+ from .workflow_contracts import (
32
+ AdmissionBinding, BackendDescriptor, Criterion, Dataset, DatasetDefinition,
33
+ EvaluatorDefinition, MetricSpec, PayloadSchema, PlanBinding,
34
+ ReferenceBinding, ResolvedBinding, SuiteDefinition, WorkflowContractError,
35
+ contract_digest,
36
+ )
37
+ from .workflow_registry import WorkflowRegistry
38
+
39
+
40
+ RERANK_BACKEND_ID = "sphynx.rerank.local"
41
+ RERANK_REFERENCE_SCHEMA = "sphynx.rag.rerank.case-references.v1"
42
+ _AGGREGATION_POLICY_ID = "sphynx.rerank.aggregate.v1"
43
+ _COMPARISON_POLICY_ID = "sphynx.rerank.paired-comparison.v1"
44
+ _DESCRIPTOR = BackendDescriptor(
45
+ backend_id=RERANK_BACKEND_ID, version="v1",
46
+ implementation_id="sphynx.rag.rerank.native-adapter.v1",
47
+ capabilities=("sphynx.case-inputs.v1", "sphynx.local-subprocess.v1",
48
+ "sphynx.saved-assessment.v1", "sphynx.native-report.v1",
49
+ "sphynx.paired-comparison.v1"),
50
+ supported_scopes=("component",), supported_modes=("local",), suite_ids=(PACK_ID,),
51
+ metadata={"native_pack_id": PACK_ID, "trust_tier": "local_unverified",
52
+ "illustrative": True, "candidate_source_bytes_verified": False},
53
+ )
54
+
55
+
56
+ def _candidate_identity(candidate: CandidateSpec) -> dict[str, str]:
57
+ # The native v1 identity pins argv metadata, not executable/source bytes.
58
+ return {"name": candidate.name, "version": candidate.version,
59
+ "interface_version": candidate.interface_version,
60
+ "argv_sha256": sha256_bytes(canonical_json(list(candidate.argv)).encode("utf-8"))}
61
+
62
+
63
+ def _case_view(case: Any) -> CaseView:
64
+ request = build_candidate_request(case, PACK_ID)
65
+ return CaseView(
66
+ case.case_id, REQUEST_SCHEMA_VERSION, request.to_dict(),
67
+ metadata={"dataset_id": case.dataset_id, "dataset_version": case.dataset_version,
68
+ "pool_digest": case.pool_digest, "split": case.split,
69
+ "split_group_id": case.split_group_id, "cluster_id": case.cluster_id},
70
+ )
71
+
72
+
73
+ class RerankBackend:
74
+ """Delegate the common flow to unchanged native reranking operations."""
75
+
76
+ @property
77
+ def descriptor(self) -> BackendDescriptor:
78
+ return _DESCRIPTOR
79
+
80
+ def describe(self, suite_id: str) -> SuiteDefinition:
81
+ """Read declared metadata only; do not load cases or grading labels."""
82
+ if suite_id != PACK_ID:
83
+ raise WorkflowContractError(f"no native reranking adapter for suite {suite_id!r}")
84
+ invocation = resolve_eval(suite_id)
85
+ descriptor = get_pack(suite_id)
86
+ artifact = invocation.artifacts[0]
87
+ dataset = DatasetDefinition(
88
+ dataset_id=str(artifact["ref"]).rsplit("@", 1)[0],
89
+ version=artifact["version"], provider_id="sphynx.rerank.frozen-pool.v1",
90
+ input_schema_id=REQUEST_SCHEMA_VERSION, reference_schema_id=RERANK_REFERENCE_SCHEMA,
91
+ selection={"membership": "all_installed_cases", "order": "native_pack_order"},
92
+ identity={"artifact_ref": artifact["ref"], "sha256": artifact["digest"]},
93
+ )
94
+ return SuiteDefinition(
95
+ suite_id=suite_id, version=descriptor.pack_release,
96
+ backend_id=self.descriptor.backend_id, title=descriptor.title,
97
+ criterion_ids=invocation.definition.metric_ids,
98
+ evaluator_ids=(GRADER_RELEASE,), metric_ids=invocation.definition.metric_ids,
99
+ required_capabilities=("sphynx.local-subprocess.v1", "sphynx.saved-assessment.v1"),
100
+ datasets=(dataset,),
101
+ metadata={"native_definition_digest": invocation.definition.definition_digest,
102
+ "native_metric_criterion_mapping": True,
103
+ "native_execution_policy": asdict(descriptor.execution_policy),
104
+ "case_count": descriptor.case_count, "illustrative": True,
105
+ "trust_tier": "local_unverified"},
106
+ )
107
+
108
+ def _suite(self, suite: SuiteDefinition) -> None:
109
+ if not isinstance(suite, SuiteDefinition):
110
+ raise WorkflowContractError("native reranking suite must be SuiteDefinition")
111
+ expected = self.describe(suite.suite_id)
112
+ if suite.definition_digest != expected.definition_digest:
113
+ raise WorkflowContractError("suite definition differs from the native reranking adapter")
114
+
115
+ def _plan(self, plan: PlanBinding) -> None:
116
+ if not isinstance(plan, PlanBinding):
117
+ raise WorkflowContractError("native reranking plan must be PlanBinding")
118
+ self._suite(plan.suite)
119
+ if plan.scope not in self.descriptor.supported_scopes or plan.mode not in self.descriptor.supported_modes:
120
+ raise WorkflowContractError("this suite supports only scope='component', mode='local'")
121
+ if not isinstance(plan.candidate, CandidateSpec):
122
+ raise WorkflowContractError("native reranking candidate must be CandidateSpec")
123
+ check = check_candidate(plan.candidate, plan.suite.suite_id)
124
+ if not check.compatible or not check.pack_runnable:
125
+ raise WorkflowContractError("; ".join(check.reasons) or "native suite cannot execute")
126
+ if plan.runtime:
127
+ raise WorkflowContractError("native reranking does not accept runtime overrides")
128
+
129
+ def cases(self, suite: SuiteDefinition) -> tuple[CaseView, ...]:
130
+ self._suite(suite)
131
+ return tuple(_case_view(case) for case in load_rag_rerank_pack().cases)
132
+
133
+ def resolve(self, plan: PlanBinding) -> ResolvedBinding:
134
+ self._plan(plan)
135
+ invocation = resolve_eval(plan.suite.suite_id, plan.parameters)
136
+ return ResolvedBinding(
137
+ identity={"backend": self.descriptor.to_dict(),
138
+ "suite_id": plan.suite.suite_id,
139
+ "suite_definition_digest": plan.suite.definition_digest,
140
+ "scope": plan.scope, "mode": plan.mode, "runtime": dict(plan.runtime),
141
+ "candidate": _candidate_identity(plan.candidate),
142
+ "native_invocation": invocation.to_dict(),
143
+ "candidate_source_bytes_verified": False},
144
+ native_ref=invocation,
145
+ )
146
+
147
+ def _resolution(self, plan: PlanBinding, resolved: ResolvedBinding) -> None:
148
+ if not isinstance(resolved, ResolvedBinding) or not isinstance(resolved.native_ref, ResolvedInvocation):
149
+ raise WorkflowContractError("native reranking requires a native resolved invocation")
150
+ expected = self.resolve(plan)
151
+ if (resolved.resolution_digest != expected.resolution_digest
152
+ or resolved.native_ref.invocation_digest != expected.native_ref.invocation_digest):
153
+ raise WorkflowContractError("resolved suite or candidate identity changed; resolve the plan again")
154
+
155
+ def _facts(self, plan: PlanBinding, resolved: ResolvedBinding) -> dict[str, Any]:
156
+ self._resolution(plan, resolved)
157
+ admitted = require_legacy_rerank(plan.suite.suite_id, plan.parameters)
158
+ if admitted.invocation_digest != resolved.native_ref.invocation_digest:
159
+ raise WorkflowContractError("native admission differs from the resolved invocation")
160
+ # A cached successful load cannot establish the identity of current bytes.
161
+ payload = resources.files("sphynx").joinpath("data", "rag-rerank-v1.json").read_bytes()
162
+ digest = sha256_bytes(payload)
163
+ if digest != "sha256:" + FIXTURE_SHA256:
164
+ raise WorkflowContractError("installed suite bytes differ from the pinned release")
165
+ pack = load_rag_rerank_pack()
166
+ if pack.dataset_digest != digest:
167
+ raise WorkflowContractError("loaded suite identity differs from verified asset bytes")
168
+ verified_cases, _, _ = validate_dataset_v2(json.loads(payload))
169
+ if pack.cases != verified_cases:
170
+ raise WorkflowContractError("loaded case membership differs from verified suite bytes")
171
+ descriptor = get_pack(plan.suite.suite_id)
172
+ if descriptor != pack.descriptor:
173
+ raise WorkflowContractError("suite descriptor differs from the loaded native pack")
174
+ return {"dataset_digest": digest, "descriptor_digest": contract_digest(asdict(descriptor)),
175
+ "case_ids": [case.case_id for case in pack.cases]}
176
+
177
+ def admit(self, plan: PlanBinding, resolved: ResolvedBinding) -> AdmissionBinding:
178
+ return AdmissionBinding(
179
+ resolution_digest=resolved.resolution_digest, facts=self._facts(plan, resolved),
180
+ trust_tier="local_unverified", artifact_bytes_verified=True,
181
+ candidate_source_bytes_verified=False,
182
+ )
183
+
184
+ def recheck(self, plan: PlanBinding, resolved: ResolvedBinding, admission: AdmissionBinding) -> None:
185
+ if not isinstance(admission, AdmissionBinding):
186
+ raise WorkflowContractError("native reranking admission must be AdmissionBinding")
187
+ expected = self.admit(plan, resolved)
188
+ if contract_digest(admission.to_dict()) != contract_digest(expected.to_dict()):
189
+ raise WorkflowContractError("admitted suite has changed; resolve and admit a new plan")
190
+
191
+ def execute(self, plan: PlanBinding, resolved: ResolvedBinding, admission: AdmissionBinding,
192
+ output_dir: str | Path | None) -> RunRef:
193
+ self.recheck(plan, resolved, admission)
194
+ if output_dir is None:
195
+ raise WorkflowContractError("native reranking requires an output directory")
196
+ return native_run(plan.candidate, plan.suite.suite_id, output_dir,
197
+ parameters=plan.parameters if plan.parameters else None)
198
+
199
+ def accepts_reference(self, native_ref: Any, kind: str) -> bool:
200
+ if kind == "run":
201
+ return isinstance(native_ref, RunRef) and native_ref.pack_id == PACK_ID
202
+ if kind == "grading":
203
+ if not isinstance(native_ref, EvaluationRef):
204
+ return False
205
+ if native_ref.pack_id == PACK_ID:
206
+ return True
207
+ # A wholly unreadable native run cannot supply a pack or grading
208
+ # path. Keep its explicit error diagnostic for an already-bound
209
+ # backend; this does not recognize foreign packs or successful data.
210
+ return (native_ref.pack_id is None and native_ref.path is None
211
+ and native_ref.status in {"corrupt", "incompatible", "unsupported_schema", "grader_error"})
212
+ return False
213
+
214
+ def _reference(self, native_ref: Any, kind: str) -> None:
215
+ if not self.accepts_reference(native_ref, kind):
216
+ raise WorkflowContractError(f"unsupported native reranking {kind} reference")
217
+
218
+ def reference(self, native_ref: Any, kind: str) -> ReferenceBinding:
219
+ self._reference(native_ref, kind)
220
+ if kind == "run":
221
+ manifest = read_run_manifest(native_ref)
222
+ return ReferenceBinding(
223
+ record_id=native_ref.run_id, run_id=native_ref.run_id,
224
+ status=manifest["status"], trust_tier="local_unverified",
225
+ metadata={"native_pack_id": native_ref.pack_id, "illustrative": True},
226
+ )
227
+ return ReferenceBinding(
228
+ record_id=native_ref.evaluation_id, run_id=native_ref.run_id,
229
+ status=native_ref.status, trust_tier="local_unverified",
230
+ metadata={"native_pack_id": native_ref.pack_id, "reason": native_ref.reason,
231
+ "illustrative": True},
232
+ )
233
+
234
+ def assess(self, native_run: Any) -> EvaluationRef:
235
+ self._reference(native_run, "run")
236
+ return native_grade(native_run)
237
+
238
+ def report(self, native_grading: Any) -> Scorecard:
239
+ self._reference(native_grading, "grading")
240
+ if native_grading.path is None:
241
+ raise WorkflowContractError(native_grading.reason or "no readable saved grading record")
242
+ return native_report(native_grading)
243
+
244
+ def assessments(self, native_grading: Any, case_id: str | None = None) -> tuple[Assessment, ...]:
245
+ self._reference(native_grading, "grading")
246
+ if native_grading.path is None:
247
+ raise WorkflowContractError(native_grading.reason or "no readable saved grading record")
248
+ path = native_grading.path / "judgments.jsonl"
249
+ try:
250
+ raw = path.read_bytes()
251
+ except OSError as exc:
252
+ raise ResultError(f"cannot read saved assessments: {exc}") from exc
253
+ data = self.report(native_grading).to_dict()
254
+ if path.read_bytes() != raw:
255
+ raise WorkflowContractError("saved assessments changed during verification; read the report again")
256
+ rows = [json.loads(line) for line in raw.decode("utf-8").splitlines()]
257
+ if case_id is not None and case_id not in {row["case_id"] for row in rows}:
258
+ raise WorkflowContractError(f"case {case_id!r} is not in this report")
259
+ assessments = []
260
+ for row in rows:
261
+ if case_id is not None and row["case_id"] != case_id:
262
+ continue
263
+ for metric_id, value in row["values"].items():
264
+ reason = row["undefined_reasons"].get(metric_id) or row["reason"]
265
+ if value is not None:
266
+ availability = "available"
267
+ elif row["status"] == "grader_error":
268
+ availability = "error"
269
+ elif row["status"] in {"corrupt", "incompatible", "unsupported_schema"}:
270
+ availability = "unavailable"
271
+ elif reason in {"unsupported_query", "zero_ideal_dcg"}:
272
+ availability = "undefined"
273
+ else:
274
+ availability = "missing"
275
+ assessments.append(Assessment(
276
+ criterion_id=metric_id, evaluator_id=data["releases"]["grader_release"],
277
+ case_id=row["case_id"], kind="score", availability=availability,
278
+ value=value, reason=reason,
279
+ evidence_ids=(row["capture_id"],) if row["capture_id"] else (),
280
+ run_id=row["run_id"], grading_id=row["evaluation_id"],
281
+ trial=row["trial_index"], attempt=1,
282
+ ))
283
+ return tuple(assessments)
284
+
285
+ def compare(self, baseline_grading: Any, candidate_grading: Any) -> Comparison:
286
+ self._reference(baseline_grading, "grading")
287
+ self._reference(candidate_grading, "grading")
288
+ return native_compare(baseline_grading, candidate_grading)
289
+
290
+ def compare_view(self, baseline_grading, candidate_grading):
291
+ from ._comparison_adapters import _rerank_view, _sources
292
+ native = self.compare(baseline_grading, candidate_grading).to_dict()
293
+ reports = [self.report(ref).to_dict() for ref in (baseline_grading, candidate_grading)]
294
+ conditions = [{key: report[key] for key in ("identity", "releases", "candidate")}
295
+ for report in reports]
296
+ return _rerank_view(_sources(self, baseline_grading, candidate_grading, conditions=conditions),
297
+ native, {item.metric_id: _metric_spec(item) for item in metric_definitions()})
298
+
299
+
300
+ def _validate_native_request(value: Any) -> None:
301
+ """Wrap the strict typed native parser without changing validator return policy."""
302
+ validate_candidate_request(value)
303
+
304
+
305
+ def _validate_native_references(value: Any) -> None:
306
+ """Validate the reference structure without loading or authorizing native labels.
307
+
308
+ Exact case/cohort/label binding remains the native grader's responsibility.
309
+ The public default provider never supplies a reference loader or grant.
310
+ """
311
+ if type(value) is not dict or set(value) != {"case_id", "label_authority", "candidates", "evidence_groups"}:
312
+ raise WorkflowContractError("native references require the exact CaseReferences fields")
313
+ if type(value["candidates"]) is not list or not value["candidates"]:
314
+ raise WorkflowContractError("native references require a nonempty candidate array")
315
+ if type(value["evidence_groups"]) is not list:
316
+ raise WorkflowContractError("native references require an evidence-group array")
317
+ candidate_fields = {"candidate_id", "relevance_grade", "evidence_group_ids"}
318
+ group_fields = {"evidence_group_id", "description", "required", "acceptable_candidate_ids"}
319
+ if any(type(item) is not dict or set(item) != candidate_fields for item in value["candidates"]):
320
+ raise WorkflowContractError("native reference candidates have unsupported fields")
321
+ if any(type(item) is not dict or set(item) != group_fields for item in value["evidence_groups"]):
322
+ raise WorkflowContractError("native evidence groups have unsupported fields")
323
+ references = CaseReferences(
324
+ value["case_id"], value["label_authority"],
325
+ tuple(ReferenceCandidate(**item) for item in value["candidates"]),
326
+ tuple(EvidenceGroup(**item) for item in value["evidence_groups"]),
327
+ )
328
+ candidates = {item.candidate_id: item for item in references.candidates}
329
+ groups = {item.evidence_group_id: item for item in references.evidence_groups}
330
+ if any(not set(item.evidence_group_ids).issubset(groups) for item in references.candidates):
331
+ raise WorkflowContractError("native reference candidate names an unknown evidence group")
332
+ if any(not set(item.acceptable_candidate_ids).issubset(candidates) for item in references.evidence_groups):
333
+ raise WorkflowContractError("native evidence group names an unknown candidate")
334
+ if any((group_id in candidate.evidence_group_ids) != (candidate_id in group.acceptable_candidate_ids)
335
+ for candidate_id, candidate in candidates.items() for group_id, group in groups.items()):
336
+ raise WorkflowContractError("native candidate and evidence-group links must be reciprocal")
337
+
338
+
339
+ def _validate_builtin_assessment(value: Any) -> None:
340
+ fields = {"schema_version", "criterion_id", "evaluator_id", "case_id", "kind", "availability",
341
+ "value", "reason", "evidence_ids", "run_id", "grading_id", "trial", "attempt"}
342
+ if type(value) is not dict or set(value) != fields or value["schema_version"] != ASSESSMENT_VIEW_SCHEMA:
343
+ raise WorkflowContractError("builtin assessments require the exact v1 view fields")
344
+ if value["kind"] not in {"assertion", "score", "label"}:
345
+ raise WorkflowContractError("builtin assessment schema does not accept custom payload kinds")
346
+ Assessment(**{key: item for key, item in value.items() if key != "schema_version"})
347
+
348
+
349
+ def _metric_spec(definition):
350
+ return MetricSpec(definition.metric_id, definition.release, definition.unit, definition.direction,
351
+ aggregation_policy=_AGGREGATION_POLICY_ID, comparison_policy=_COMPARISON_POLICY_ID,
352
+ population={"eligible_population": definition.eligible_population,
353
+ "undefined_policy": definition.undefined_policy, "missing_policy": definition.missing_policy},
354
+ metadata={"native_aggregation": definition.aggregation, "primary": definition.primary})
355
+
356
+
357
+ def register_rerank_backend(registry: WorkflowRegistry) -> RerankBackend:
358
+ """Register this adapter and its exact declared contracts without reading assets.
359
+
360
+ The registry must explicitly permit the native bare metric and dataset IDs.
361
+ ``build_rerank_registry`` supplies that narrow legacy-ID list. Registration
362
+ is not admission, does not run validators, and never loads grading labels.
363
+ Existing bindings are not silently replaced; duplicate registration fails.
364
+ """
365
+ if not isinstance(registry, WorkflowRegistry):
366
+ raise WorkflowContractError("native reranking registration requires a WorkflowRegistry")
367
+ backend = RerankBackend()
368
+ suite = backend.describe(PACK_ID)
369
+ registry.register_backend(backend)
370
+ input_schema = registry.register_schema(PayloadSchema(
371
+ REQUEST_SCHEMA_VERSION, "v1", "sphynx.contracts.validate_candidate_request.v1",
372
+ _validate_native_request,
373
+ metadata={"native_protocol": REQUEST_SCHEMA_VERSION, "candidate_permitted": True},
374
+ ))
375
+ reference_schema = registry.register_schema(PayloadSchema(
376
+ RERANK_REFERENCE_SCHEMA, "v1", "sphynx.rerank.native-case-references.v1",
377
+ _validate_native_references,
378
+ metadata={"access": "backend_authorized_only", "native_grader_binds_exact_labels": True},
379
+ ))
380
+ registry.register_schema(PayloadSchema(
381
+ ASSESSMENT_VIEW_SCHEMA, "v1", "sphynx.assessment-contracts.builtin-v1",
382
+ _validate_builtin_assessment,
383
+ metadata={"kinds": ["assertion", "score", "label"], "native_records_authoritative": True},
384
+ ))
385
+ registry.register_provider(Dataset(
386
+ definition=suite.datasets[0], input_schema=input_schema,
387
+ case_source=lambda selected: backend.cases(suite), reference_schema=reference_schema,
388
+ ))
389
+ registry.register_policy(
390
+ _AGGREGATION_POLICY_ID, version="v1",
391
+ implementation_id=METRIC_RELEASE + ".aggregate-case-metrics", kind="aggregation",
392
+ metadata={"execution": "backend_owned", "native_operation": "sphynx.metrics.aggregate_case_metrics",
393
+ "preserve_eligible_missing_undefined_populations": True},
394
+ )
395
+ registry.register_policy(
396
+ _COMPARISON_POLICY_ID, version="v1", implementation_id="sphynx.results.compare.v1",
397
+ kind="comparison", metadata={"execution": "backend_owned",
398
+ "native_operation": "sphynx.results.compare",
399
+ "delta_direction": "candidate_minus_baseline"},
400
+ )
401
+ for definition in metric_definitions():
402
+ registry.register_criterion(Criterion(
403
+ definition.metric_id, definition.release, definition.metric_id,
404
+ configuration={"native_metric_mapping": True,
405
+ "dimension": "measurement" if definition.metric_id == "runner_latency_ms" else "quality"},
406
+ ))
407
+ registry.register_metric(_metric_spec(definition))
408
+ registry.register_evaluator(EvaluatorDefinition(
409
+ GRADER_RELEASE, "v1", "sphynx.results.grade:" + GRADER_RELEASE,
410
+ criterion_ids=suite.criterion_ids, assessment_schema_ids=(ASSESSMENT_VIEW_SCHEMA,),
411
+ metric_ids=suite.metric_ids, required_capabilities=("sphynx.saved-assessment.v1",),
412
+ configuration={"execution": "backend_owned", "native_grading_records_authoritative": True},
413
+ ))
414
+ registry.register_suite(suite)
415
+ return backend
416
+
417
+
418
+ def build_rerank_registry() -> WorkflowRegistry:
419
+ """Return a fresh registry for the built-in native reranking adapter."""
420
+ suite = RerankBackend().describe(PACK_ID)
421
+ registry = WorkflowRegistry(legacy_ids=(*suite.criterion_ids, *suite.metric_ids,
422
+ *(dataset.dataset_id for dataset in suite.datasets)))
423
+ register_rerank_backend(registry)
424
+ return registry
@@ -0,0 +1,190 @@
1
+ """Pinned operator-side candidate entry point for local research parity.
2
+
3
+ This protocol uses an installed SDK and owner-provided assets. It is not an
4
+ admission into the hosted worker's smaller repository/runtime allowlist.
5
+ Configuration contains file references and digests, never credential values.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ from .contracts import CandidateSpec, ContractError, canonical_json, validate_candidate_request
15
+ from .integration_contracts import CandidateConfiguration, ComparisonIdentity, _freeze, _wire, require_digest, value_digest
16
+ from .model_adapters import create_model_scorer
17
+ from .recorded_scores import create_recorded_scorer
18
+
19
+ SCHEMA = "sphynx.research-candidate.v1"
20
+ INTERFACE = "sphynx.rag.rerank.v1"
21
+
22
+
23
+ def _json(raw):
24
+ def pairs(items):
25
+ result = {}
26
+ for key, value in items:
27
+ if key in result:
28
+ raise ContractError("duplicate JSON field")
29
+ result[key] = value
30
+ return result
31
+ def invalid(value):
32
+ raise ContractError("nonfinite JSON value")
33
+ return json.loads(raw, object_pairs_hook=pairs, parse_constant=invalid)
34
+
35
+
36
+ def _read(path, expected, limit=128 * 1024 * 1024):
37
+ require_digest(expected, "asset digest")
38
+ source = Path(path).expanduser().resolve(strict=True)
39
+ if not source.is_file() or source.stat().st_size > limit:
40
+ raise ContractError("asset must be a bounded file")
41
+ raw = source.read_bytes()
42
+ if "sha256:" + hashlib.sha256(raw).hexdigest() != expected:
43
+ raise ContractError("asset bytes differ from pinned digest")
44
+ return _json(raw)
45
+
46
+
47
+ def _fields(value, keys):
48
+ if not isinstance(value, dict) or set(value) != set(keys):
49
+ raise ContractError("research candidate configuration fields differ")
50
+ _freeze(value, reject_secrets=True)
51
+ return value
52
+
53
+
54
+ def _source(value):
55
+ if not isinstance(value, dict):
56
+ raise ContractError("source scorer configuration required")
57
+ if value.get("kind") == "offline_model":
58
+ _fields(value, ("kind", "model_id", "snapshot_path", "snapshot_digest", "threads"))
59
+ return create_model_scorer(value["model_id"], value["snapshot_path"], value["snapshot_digest"], threads=value["threads"])
60
+ if value.get("kind") == "recorded_model":
61
+ _fields(value, ("kind", "recording_path", "recording_digest", "requests_path", "requests_digest"))
62
+ artifact = _read(value["recording_path"], value["recording_digest"])
63
+ requests = _read(value["requests_path"], value["requests_digest"])
64
+ if not isinstance(requests, list):
65
+ raise ContractError("recorded source requires a complete ordered request array")
66
+ checked = [validate_candidate_request(request).to_dict() for request in requests]
67
+ return create_recorded_scorer(value["recording_path"], value["recording_digest"],
68
+ identity=ComparisonIdentity.from_dict(artifact["identity"]), requests=checked, expected_model=artifact["model"])
69
+ raise ContractError("source kind must be offline_model or recorded_model")
70
+
71
+
72
+ class RecordedFeatures:
73
+ """Complete request-bound, label-free learned features; no guessed joins."""
74
+ def __init__(self, config):
75
+ from .learned_rerank import FEATURE_NAMES
76
+ _fields(config, ("path", "digest"))
77
+ self.path, self.digest = config["path"], config["digest"]
78
+ artifact = _read(self.path, self.digest)
79
+ _fields(artifact, ("schema_version", "cases"))
80
+ if artifact["schema_version"] != "sphynx.rerank-features.v1" or not isinstance(artifact["cases"], list):
81
+ raise ContractError("unsupported recorded feature schema")
82
+ rows = {}
83
+ for row in artifact["cases"]:
84
+ _fields(row, ("case_id", "request_digest", "features"))
85
+ require_digest(row["request_digest"], "feature request digest")
86
+ if not isinstance(row["case_id"], str) or row["case_id"] in rows or not isinstance(row["features"], dict):
87
+ raise ContractError("feature case identity differs")
88
+ for features in row["features"].values():
89
+ _fields(features, FEATURE_NAMES)
90
+ if any(value is not None and (type(value) not in (int, float)) for value in features.values()):
91
+ raise ContractError("feature values must be finite numbers or explicit null")
92
+ rows[row["case_id"]] = row
93
+ self._rows = _freeze(rows)
94
+ self.configuration = CandidateConfiguration("recorded-features-v1", "sphynx.rerank-features.v1",
95
+ "sha256:" + hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), {"asset_digest": self.digest}, "recorded")
96
+
97
+ def __call__(self, request):
98
+ _read(self.path, self.digest)
99
+ row = self._rows.get(request["case_id"])
100
+ if row is None or row["request_digest"] != value_digest(request):
101
+ raise ContractError("feature recording request differs")
102
+ if set(row["features"]) != {candidate["candidate_id"] for candidate in request["candidates"]}:
103
+ raise ContractError("feature passage membership differs")
104
+ return _wire(row["features"])
105
+
106
+
107
+ def load_research_candidate(configuration_path, expected_digest):
108
+ """Construct a frozen reranker without loading weights or calling providers."""
109
+ from . import __version__
110
+ config = _read(configuration_path, expected_digest, 1024 * 1024)
111
+ _fields(config, ("schema_version", "sdk_version", "family", "method", "parameters", "sources", "fitted_state", "features"))
112
+ if config["schema_version"] != SCHEMA or config["sdk_version"] != __version__:
113
+ raise ContractError("research candidate schema or installed SDK version differs")
114
+ if not isinstance(config["sources"], dict) or not isinstance(config["parameters"], dict):
115
+ raise ContractError("research candidate sources and parameters must be objects")
116
+ sources = {name: _source(value) for name, value in config["sources"].items()}
117
+ family, method = config["family"], config["method"]
118
+ if family in ("statistical", "learned"):
119
+ for slot, source in sources.items():
120
+ params = source.configuration.parameters
121
+ model = params.get("model", params)
122
+ model_id = model.get("id", model.get("model_id"))
123
+ if model_id != {"minilm": "minilm-l6-v2", "jina": "jina-turbo-v1"}.get(slot):
124
+ raise ContractError("source model identity differs from selected family slot")
125
+ if model.get("activation") != "identity":
126
+ raise ContractError("family source requires raw identity-activation logits")
127
+ if family == "pointwise":
128
+ if set(sources) != {"model"} or config["parameters"] or config["fitted_state"] is not None or config["features"] is not None:
129
+ raise ContractError("pointwise candidate must declare exactly one source")
130
+ source = sources["model"]
131
+ if source.configuration.parameters.get("model_id", source.configuration.parameters.get("model", {}).get("id")) != method:
132
+ raise ContractError("selected pointwise method differs from source model")
133
+ return source
134
+ if family == "statistical":
135
+ from .statistical_rerank import create_statistical_scorer, METHOD_REGISTRY
136
+ if config["fitted_state"] is not None or config["features"] is not None or set(sources) - {"minilm", "jina"}:
137
+ raise ContractError("statistical source fields differ")
138
+ if method not in METHOD_REGISTRY:
139
+ raise ContractError("unknown statistical method")
140
+ required = set() if method in ("S1", "Dense-only") else ({"minilm", "jina"} if method == "Guarded-Jina-E2"
141
+ else {"jina"} if method.startswith(("Jina", "S6")) or method == "S7" else {"minilm"})
142
+ if set(sources) != required:
143
+ raise ContractError("selected statistical method requires its exact source scorers")
144
+ return create_statistical_scorer(method, scorers=sources, parameters=config["parameters"])
145
+ if family == "learned":
146
+ from .learned_rerank import create_learned_scorer
147
+ from .rerank_family_contracts import load_fitted_state
148
+ state = _fields(config["fitted_state"], ("path", "digest"))
149
+ params = _fields(config["parameters"], ("evaluation_cohort_digest", "domain_id"))
150
+ if set(sources) != {"minilm"}:
151
+ raise ContractError("learned candidate requires the MiniLM source")
152
+ fitted = load_fitted_state(state["path"], state["digest"])
153
+ if fitted.method != method:
154
+ raise ContractError("selected learned method differs from fitted state")
155
+ return create_learned_scorer(fitted, minilm_scorer=sources["minilm"],
156
+ feature_provider=RecordedFeatures(config["features"]), **params)
157
+ raise ContractError("unsupported research candidate family")
158
+
159
+
160
+ def make_research_candidate(configuration_path, expected_digest):
161
+ """Return the existing SDK subprocess protocol with pinned configuration."""
162
+ scorer = load_research_candidate(configuration_path, expected_digest)
163
+ return CandidateSpec(scorer.configuration.configuration_id, scorer.configuration.digest,
164
+ (sys.executable, "-m", "sphynx.research_candidate", str(Path(configuration_path).resolve()), expected_digest,
165
+ scorer.configuration.digest), INTERFACE)
166
+
167
+
168
+ def protocol_main(argv=None):
169
+ try:
170
+ args = sys.argv[1:] if argv is None else argv
171
+ if len(args) not in (2, 3):
172
+ raise ContractError("expected configuration path, exact digest and optional resolved scorer digest")
173
+ scorer = load_research_candidate(args[0], args[1])
174
+ if len(args) == 3:
175
+ require_digest(args[2], "resolved scorer digest")
176
+ if scorer.configuration.digest != args[2]:
177
+ raise ContractError("resolved scorer configuration changed before execution")
178
+ raw = sys.stdin.buffer.read(1024 * 1024 + 1)
179
+ if len(raw) > 1024 * 1024:
180
+ raise ContractError("request too large")
181
+ request = validate_candidate_request(_json(raw)).to_dict()
182
+ print(canonical_json(scorer(request)), flush=True)
183
+ return 0
184
+ except Exception as exc:
185
+ print("research_candidate_failure:" + type(exc).__name__, file=sys.stderr, flush=True)
186
+ return 2
187
+
188
+
189
+ if __name__ == "__main__":
190
+ raise SystemExit(protocol_main())