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,199 @@
1
+ """Neutral replacement identities and permitted, occurrence-bound stage evidence.
2
+
3
+ These views do not authenticate bytes or authorize access. A backend verifies
4
+ the native capture and its access policy before projecting a selected stage.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import InitVar, dataclass, field
9
+ import hashlib
10
+ import json
11
+ import re
12
+ from typing import Any, Mapping, Protocol, runtime_checkable
13
+
14
+ from .assessment_contracts import EvidenceView, _freeze_json, _mapping, _thaw
15
+ from .workflow_contracts import PayloadSchema, WorkflowContractError, _text, contract_digest
16
+
17
+ STAGE_EVIDENCE_CAPABILITY = "sphynx.stage-evidence.v1"
18
+ _DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z")
19
+ _PROJECTED = object()
20
+
21
+
22
+ class StageContractError(WorkflowContractError):
23
+ """An invalid selection, replacement or stage projection."""
24
+
25
+
26
+ def _digest(value, name):
27
+ if type(value) is not str or not _DIGEST.fullmatch(value):
28
+ raise StageContractError(name + " must be a complete sha256 digest")
29
+
30
+
31
+ def _positive(value, name):
32
+ if type(value) is not int or value < 1:
33
+ raise StageContractError(name + " must be a positive integer")
34
+
35
+
36
+ def _projection_digest(value):
37
+ raw = json.dumps(_thaw(value), ensure_ascii=False, sort_keys=True,
38
+ separators=(",", ":"), allow_nan=False).encode("utf-8")
39
+ return "sha256:" + hashlib.sha256(raw).hexdigest()
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class StageDefinition:
44
+ """One logical stage with exact implementation/configuration and I/O IDs."""
45
+
46
+ stage_id: str
47
+ implementation_id: str
48
+ version: str
49
+ input_schema_id: str
50
+ output_schema_id: str
51
+ implementation_digest: str
52
+ configuration_digest: str
53
+ metadata: Mapping[str, Any] = field(default_factory=dict)
54
+
55
+ def __post_init__(self):
56
+ for name in ("stage_id", "implementation_id", "version", "input_schema_id", "output_schema_id"):
57
+ _text(getattr(self, name), name)
58
+ for name in ("implementation_digest", "configuration_digest"):
59
+ _digest(getattr(self, name), name)
60
+ object.__setattr__(self, "metadata", _mapping(self.metadata, "stage metadata"))
61
+
62
+ @property
63
+ def identity_digest(self):
64
+ return contract_digest(self.to_dict())
65
+
66
+ def to_dict(self):
67
+ return {"stage_id": self.stage_id, "implementation_id": self.implementation_id,
68
+ "version": self.version, "input_schema_id": self.input_schema_id,
69
+ "output_schema_id": self.output_schema_id,
70
+ "implementation_digest": self.implementation_digest,
71
+ "configuration_digest": self.configuration_digest, "metadata": _thaw(self.metadata)}
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class StageReplacement:
76
+ """An explicit change at one compatible stage; it does not execute a swap."""
77
+
78
+ stage_id: str
79
+ baseline: StageDefinition
80
+ replacement: StageDefinition
81
+
82
+ def __post_init__(self):
83
+ _text(self.stage_id, "stage_id")
84
+ if not isinstance(self.baseline, StageDefinition) or not isinstance(self.replacement, StageDefinition):
85
+ raise StageContractError("replacement requires exact stage definitions")
86
+ if self.baseline.stage_id != self.stage_id or self.replacement.stage_id != self.stage_id:
87
+ raise StageContractError("replacement stage identity differs")
88
+ if (self.baseline.input_schema_id, self.baseline.output_schema_id) != (
89
+ self.replacement.input_schema_id, self.replacement.output_schema_id):
90
+ raise StageContractError("replacement input/output schemas are incompatible")
91
+ if self.baseline.identity_digest == self.replacement.identity_digest:
92
+ raise StageContractError("replacement must change stage identity")
93
+
94
+ def require_baseline(self, current: StageDefinition):
95
+ if not isinstance(current, StageDefinition) or current.identity_digest != self.baseline.identity_digest:
96
+ raise StageContractError("replacement baseline identity drift")
97
+
98
+ def to_dict(self):
99
+ return {"stage_id": self.stage_id, "baseline": self.baseline.to_dict(),
100
+ "replacement": self.replacement.to_dict()}
101
+
102
+
103
+ @dataclass(frozen=True)
104
+ class StageSelector:
105
+ """Select one exact stage occurrence within one case/trial/attempt.
106
+
107
+ No wildcard or inferred 'latest attempt' is allowed. Different evidence roles
108
+ may be returned for that occurrence; their native visibility remains binding.
109
+ """
110
+
111
+ case_id: str
112
+ trial: int
113
+ attempt: int
114
+ stage_id: str
115
+ occurrence: int = 1
116
+
117
+ def __post_init__(self):
118
+ _text(self.case_id, "case_id")
119
+ _text(self.stage_id, "stage_id")
120
+ for name in ("trial", "attempt", "occurrence"):
121
+ _positive(getattr(self, name), name)
122
+
123
+ def to_dict(self):
124
+ return {"case_id": self.case_id, "trial": self.trial, "attempt": self.attempt,
125
+ "stage_id": self.stage_id, "occurrence": self.occurrence}
126
+
127
+
128
+ @dataclass(frozen=True)
129
+ class StageEvidence:
130
+ """A permitted projection, distinct from its native artifact byte identity."""
131
+
132
+ selector: StageSelector
133
+ evidence: EvidenceView
134
+ projection: Any = None
135
+ projection_digest: str | None = None
136
+ projection_schema: Mapping[str, Any] | None = None
137
+ reason: str | None = None
138
+ _token: InitVar[object] = None
139
+
140
+ def __post_init__(self, _token):
141
+ if not isinstance(self.selector, StageSelector) or not isinstance(self.evidence, EvidenceView):
142
+ raise StageContractError("stage evidence requires a selector and EvidenceView")
143
+ if (self.evidence.case_id, self.evidence.step_id) != (self.selector.case_id, self.selector.stage_id):
144
+ raise StageContractError("stage evidence case/stage binding differs")
145
+ if self.evidence.availability == "available":
146
+ if _token is not _PROJECTED or self.projection_schema is None:
147
+ raise StageContractError("available stage evidence requires a validated projection")
148
+ frozen = _freeze_json(self.projection, "stage projection")
149
+ _digest(self.projection_digest, "projection_digest")
150
+ if self.projection_digest != _projection_digest(frozen):
151
+ raise StageContractError("stage projection digest differs")
152
+ schema = _mapping(self.projection_schema, "projection schema")
153
+ if set(schema) != {"schema_id", "version", "implementation_id"}:
154
+ raise StageContractError("stage projection requires exact schema identity")
155
+ for name, value in schema.items():
156
+ _text(value, "projection " + name)
157
+ object.__setattr__(self, "projection", frozen)
158
+ object.__setattr__(self, "projection_schema", schema)
159
+ elif (self.projection is not None or self.projection_digest is not None
160
+ or self.projection_schema is not None or self.reason is None):
161
+ raise StageContractError("unavailable stage evidence requires a reason and no projected value")
162
+ if self.reason is not None:
163
+ _text(self.reason, "stage evidence reason")
164
+
165
+ @classmethod
166
+ def projected(cls, selector: StageSelector, evidence: EvidenceView, projection: Any, *,
167
+ schema: PayloadSchema):
168
+ """Validate permitted content explicitly; do not read or verify storage."""
169
+ if not isinstance(schema, PayloadSchema):
170
+ raise StageContractError("stage projection requires a PayloadSchema")
171
+ validated = schema.validate(projection)
172
+ return cls(selector, evidence, validated, _projection_digest(validated),
173
+ {"schema_id": schema.schema_id, "version": schema.version,
174
+ "implementation_id": schema.implementation_id}, _token=_PROJECTED)
175
+
176
+ @classmethod
177
+ def unavailable(cls, selector: StageSelector, evidence: EvidenceView, *, reason: str):
178
+ """Keep missing/skipped/unretained evidence explicit; no synthetic value."""
179
+ if not isinstance(evidence, EvidenceView) or evidence.availability == "available":
180
+ raise StageContractError("unavailable stage evidence needs an unavailable EvidenceView")
181
+ return cls(selector, evidence, reason=reason)
182
+
183
+ @property
184
+ def availability(self):
185
+ return self.evidence.availability
186
+
187
+ def to_dict(self):
188
+ return {"schema_version": "sphynx.stage-evidence.v1",
189
+ "selector": self.selector.to_dict(), "evidence": self.evidence.to_dict(),
190
+ "projection": _thaw(self.projection), "projection_digest": self.projection_digest,
191
+ "projection_schema": None if self.projection_schema is None else _thaw(self.projection_schema),
192
+ "reason": self.reason}
193
+
194
+
195
+ @runtime_checkable
196
+ class StageEvidenceBackend(Protocol):
197
+ """Optional backend capability; selection is not a permission grant."""
198
+
199
+ def stage_evidence(self, native_ref: Any, selector: StageSelector) -> tuple[StageEvidence, ...]: ...
@@ -0,0 +1,53 @@
1
+ """Content-free statistical/Cherry method discovery; no algorithm/ML imports."""
2
+ from types import MappingProxyType
3
+ from .catalog import CandidateDescriptor
4
+ from .integration_contracts import ScoreField
5
+
6
+ _SOURCE='sha256:58cbcaff4f548bddee562a2f4680cf033d6e10c58288d37f79bc5eb19bec655d'
7
+ _BASE=('S1','S2','S3','S4','S5a','S5b','S5c','A0','A0-A','A0-B','Dense-only','MiniLM-only',
8
+ 'MiniLM-robust-mean','MiniLM-robust-dominance','MiniLM-RRF','Jina-only','Jina-60/40',
9
+ 'Jina-RRF','Jina-E1','Jina-E3','Jina-E4-no-lexical','Jina-E4','Guarded-Jina-E2',
10
+ 'S6','S6-E1','S6-E2','S6-E3','S6-E4','S7')
11
+ METHOD_NAMES=_BASE+('Jina-E2-dense-anchored','Jina-E2','Jina-E2-wide','Jina-E2-model-anchored')+tuple(
12
+ f'MiniLM-W{x:02d}/{100-x:02d}' for x in range(90,0,-10))+tuple(
13
+ f'Jina-W{x:02d}/{100-x:02d}' for x in range(90,0,-10) if x!=60)
14
+ _ALIASES={'S6':'Jina-60/40','S6-E1':'Jina-E1','S6-E2':'Jina-E2','S6-E3':'Jina-E3','S6-E4':'Jina-E4','S7':'Jina-RRF',
15
+ 'A0-A':'A0','A0-B':'A0','MiniLM-robust-dominance':'MiniLM-robust-mean'}
16
+ _DISPLAY={'A0':'raw_minilm_sigmoid_diagnostic','A0-A':'raw_fused_utility','Dense-only':'clipped_dense_score',
17
+ 'S1':'clipped_dense_score','S2':'bounded_relative_tanh','S3':'bounded_relative_tanh',
18
+ 'MiniLM-robust-mean':'bounded_relative_tanh','MiniLM-RRF':'bounded_relative_tanh'}
19
+ METHOD_METADATA=MappingProxyType({name:MappingProxyType({
20
+ 'method':name,'ranking_alias_of':_ALIASES.get(name),
21
+ 'display_convention':_DISPLAY.get(name,'original_typescript_erf' if name.startswith('S') else 'original_cherry_gaussian_cdf'),
22
+ 'fitted_selection_required':name in ('S5a','S5b','S5c'),
23
+ 'model_inference_performed_by_fusion':False,
24
+ 'historical_cherry_37_parity':not name.startswith('S'),
25
+ 'fresh_guarded_confirmation_policy_ported':False,
26
+ }) for name in METHOD_NAMES})
27
+
28
+
29
+ def _fields(name):
30
+ common=(ScoreField('ranking_utility','utility','higher','ranking_utility'),)
31
+ if name=='A0':return common+(ScoreField('raw_model_sigmoid','diagnostic','higher','sigmoid_value'),)
32
+ if name=='A0-A':return common+(ScoreField('display_utility','utility','higher','ranking_utility'),)
33
+ return common+(ScoreField('pool_relative_strength','pool_relative','higher','pool_relative_strength'),)
34
+
35
+
36
+ def _prerequisites(name):
37
+ required=['complete aligned dense/model source signals and frozen scorer configuration',
38
+ 'separately authorized dataset and original evaluation policy; discovery grants no data access']
39
+ if name in ('S5a','S5b','S5c'):required.append('external frozen development-selection weight and digest; no selection is fitted automatically')
40
+ if name in ('Jina-E4','S6-E4'):required.append('original-corpus BM25 vectors for Cherry parity; candidate-pool BM25 is a distinct index policy')
41
+ if name=='Guarded-Jina-E2':required.append('SciPy linear_sum_assignment when the original ambiguity trigger fires')
42
+ if name.startswith('S'):required.append('original TypeScript display convention; aliases share ranking but can have different displays')
43
+ return tuple(required)
44
+
45
+
46
+ CANDIDATES=tuple(CandidateDescriptor(
47
+ candidate_id='statistical-'+name.replace('/','-')+'-v1',track='rerank',
48
+ interface_version='sphynx.rerank-family.v1',source_digest=_SOURCE,
49
+ supported_dataset_ids=(),execution_modes=('recorded','trusted_local'),implementation_status='available',
50
+ reason='Pure fusion code is available. Neural scores/state/data are caller prerequisites; no hosted admission is implied. Historical Cherry parity reuses recorded scores.',
51
+ local_prerequisites=_prerequisites(name),dependencies=('scipy',) if name=='Guarded-Jina-E2' else (),
52
+ score_fields=_fields(name),supported_python=('3.11','3.12','3.13','3.14'),
53
+ ) for name in METHOD_NAMES)
@@ -0,0 +1,232 @@
1
+ """Source-pinned statistical and Cherry score-fusion algorithms.
2
+
3
+ No model is loaded here. Model callbacks are explicit and already carry frozen
4
+ model/recording identity. Display strength is never called a probability.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import hashlib
9
+ import math
10
+ import re
11
+ from collections.abc import Mapping
12
+ from pathlib import Path
13
+ from types import MappingProxyType
14
+
15
+ from .contracts import ContractError
16
+ from .integration_contracts import value_digest
17
+ from .rerank_family_contracts import SOURCE_PINS, family_configuration, finite, validate_signals
18
+
19
+ EPSILON = 1e-12
20
+ E2_WEIGHTS = (.8, .7, .6, .5, .4)
21
+ E2_BANDS = {"Jina-E2-dense-anchored": (.9,.8,.7,.6,.5), "Jina-E2": E2_WEIGHTS,
22
+ "Jina-E2-wide": (.9,.8,.7,.6,.5,.4,.3,.2,.1), "Jina-E2-model-anchored": (.4,.3,.2,.1,0.)}
23
+ BASE_METHODS = ("S1","S2","S3","S4","S5a","S5b","S5c","A0","A0-A","A0-B","Dense-only","MiniLM-only","MiniLM-robust-mean","MiniLM-robust-dominance","MiniLM-RRF","Jina-only","Jina-60/40","Jina-RRF","Jina-E1","Jina-E3","Jina-E4-no-lexical","Jina-E4","Guarded-Jina-E2","S6","S6-E1","S6-E2","S6-E3","S6-E4","S7")
24
+ METHODS = BASE_METHODS + tuple(E2_BANDS) + tuple(f"MiniLM-W{x:02d}/{100-x:02d}" for x in range(90,0,-10)) + tuple(f"Jina-W{x:02d}/{100-x:02d}" for x in range(90,0,-10) if x != 60)
25
+ METHOD_REGISTRY = MappingProxyType({name: MappingProxyType({"implementation": "available", "weights_bundled": False,
26
+ "fitted_selection_required": name in ("S5a","S5b","S5c"), "optional_dependency": "scipy" if name == "Guarded-Jina-E2" else None}) for name in METHODS})
27
+
28
+
29
+ def mean(values): return sum(values) / len(values) if values else 0.
30
+ def percentile(values, fraction=.5):
31
+ ordered=sorted(values);position=fraction*(len(ordered)-1);lower=math.floor(position);upper=math.ceil(position)
32
+ return ordered[lower] if lower==upper else ordered[lower]+(ordered[upper]-ordered[lower])*(position-lower)
33
+ def deviation(values):
34
+ average=mean(values);return math.sqrt(mean([(value-average)**2 for value in values]))
35
+ def sigmoid(value):
36
+ return 1/(1+math.exp(-value)) if value>=0 else math.exp(value)/(1+math.exp(value))
37
+ def robust_scale(values):
38
+ center=percentile(values);mad=percentile([abs(value-center) for value in values]);std=deviation(values)
39
+ return 1.4826*mad if mad>EPSILON else std if std>EPSILON else 1.
40
+ def robust_normalize(values):
41
+ values=[finite(v) for v in values]
42
+ if not values:return []
43
+ center=percentile(values);scale=robust_scale(values)
44
+ return [max(-3.,min(3.,(value-center)/scale)) for value in values]
45
+ def zscores(values):
46
+ center=mean(values);std=deviation(values);scale=std if std>EPSILON else 1.
47
+ return [(value-center)/scale for value in values]
48
+ def _ts_cdf(value):
49
+ # Preserve the original TS approximation exactly, including its argument.
50
+ absolute=abs(value);t=1/(1+.3275911*absolute)
51
+ polynomial=((((1.061405429*t-1.453152027)*t+1.421413741)*t-.284496736)*t+.254829592)*t
52
+ erf=1-polynomial*math.exp(-absolute*absolute)
53
+ return .5*(1+(-erf if value<0 else erf))
54
+ def dominance(values, *, reference="statistical.ts"):
55
+ if len(values)==1:return [.5]
56
+ scale=robust_scale(values);cdf=_ts_cdf if reference=="statistical.ts" else lambda value:.5*(1+math.erf(value/math.sqrt(2)))
57
+ return [mean([cdf((current-other)/(math.sqrt(2)*scale)) for j,other in enumerate(values) if i!=j]) for i,current in enumerate(values)]
58
+ def bounded_relative(values):return [.5+.45*math.tanh(value/2) for value in robust_normalize(values)]
59
+
60
+
61
+ def candidate_pool_bm25(signals):
62
+ query=signals[0].get("query_text")
63
+ if not isinstance(query,str) or not query or any(row.get("query_text")!=query or not row.get("passage_text") for row in signals):
64
+ raise ContractError("lexical fusion requires one shared query and every passage")
65
+ token=lambda text:re.findall('[a-z0-9]+',text.lower())
66
+ terms=set(token(query));docs=[token(row['passage_text']) for row in signals];average=mean([len(doc) for doc in docs]) or 1.
67
+ scores=[]
68
+ for doc in docs:
69
+ score=0.
70
+ for term in sorted(terms):
71
+ frequency=doc.count(term)
72
+ if not frequency:continue
73
+ df=sum(term in other for other in docs);idf=math.log(1+(len(docs)-df+.5)/(df+.5))
74
+ score+=idf*frequency*2.2/(frequency+1.2*(1-.75+.75*len(doc)/average))
75
+ scores.append(score)
76
+ return scores
77
+
78
+
79
+ def _agreement(left,right):
80
+ if abs(left)<=EPSILON and abs(right)<=EPSILON:return 1.
81
+ if abs(left)<=EPSILON or abs(right)<=EPSILON:return .5
82
+ return float((left>0)==(right>0))
83
+ def _pair_agreement(left,right):return mean([_agreement(left[i]-left[j],right[i]-right[j]) for i in range(len(left)) for j in range(i+1,len(left))])
84
+ def _signal_confidence(values,peers,penalty=0.):
85
+ if len(values)<=1:return 1.
86
+ normalized=robust_normalize(values);exps=[math.exp(v-max(normalized)) for v in normalized];total=sum(exps);p=[v/total for v in exps]
87
+ concentration=1+sum(v*math.log(v) for v in p)/math.log(len(p));ordered=sorted(normalized,reverse=True)
88
+ return max(0.,min(1.,(.3*concentration+.25*math.tanh(max(0.,ordered[0]-ordered[1]))+.2*(1-math.exp(-deviation(normalized)))+.25*mean([_pair_agreement(values,peer) for peer in peers]))*(1-penalty)))
89
+ def adaptive_weights(signals,minilm_weight=.55,lexical_weight=.2):
90
+ dense=[r['dense_score'] for r in signals];mini=[r['minilm_logit'] for r in signals];lexical=candidate_pool_bm25(signals)
91
+ saturation=mean([float(sigmoid(v)<=.05 or sigmoid(v)>=.95) for v in mini])
92
+ confidence={'dense':_signal_confidence(dense,[mini,lexical]),'minilm':_signal_confidence(mini,[dense,lexical],.5*saturation),'lexical':_signal_confidence(lexical,[dense,mini])}
93
+ base={'dense':(1-lexical_weight)*(1-minilm_weight),'minilm':(1-lexical_weight)*minilm_weight,'lexical':lexical_weight}
94
+ raw={k:base[k]*(.5+confidence[k]) for k in base};active=list(base);remaining=1.;result={}
95
+ while active:
96
+ total=sum(raw[k] for k in active);provisional={k:remaining/len(active) if total<=EPSILON else remaining*raw[k]/total for k in active}
97
+ below=next((k for k in active if provisional[k]<.2-EPSILON),None)
98
+ above=next((k for k in active if provisional[k]>.6+EPSILON),None)
99
+ key=below or above
100
+ if key is None:result.update(provisional);break
101
+ result[key]=.2 if below else .6;remaining-=result[key];active.remove(key)
102
+ return result,confidence
103
+
104
+
105
+ def _ranks(values,signals,*,pool_order=False):
106
+ ordered=sorted(range(len(values)),key=lambda i:(-values[i],i if pool_order else signals[i]['dense_rank']))
107
+ output=[0]*len(values)
108
+ for rank,index in enumerate(ordered,1):output[index]=rank
109
+ return output
110
+ def _convex(dense,model,weight,*,robust=False):
111
+ normalize=robust_normalize if robust else zscores;d,m=normalize(dense),normalize(model)
112
+ return [weight*x+(1-weight)*y for x,y in zip(d,m)]
113
+ def _e2(dense,jina,signals,weights=E2_WEIGHTS,*,robust=False):
114
+ parts=[_ranks(_convex(dense,jina,w,robust=robust),signals,pool_order=True) for w in weights]
115
+ return [mean([1/(60+part[i]) for part in parts]) for i in range(len(dense))]
116
+ def bounded_merge(a0,e2,dense,jina,signals,maximum_movement=2):
117
+ """Use the original source's SciPy assignment solver, loaded only on demand."""
118
+ try:from scipy.optimize import linear_sum_assignment
119
+ except ImportError:raise ContractError("Guarded-Jina-E2 requires optional scipy for source-identical assignment") from None
120
+ a0r=_ranks(a0,signals,pool_order=True);e2r=_ranks(e2,signals,pool_order=True)
121
+ top=lambda v:max(range(len(v)),key=lambda i:v[i]);at,dt,jt=top(a0),top(dense),top(jina)
122
+ agreed=dt==jt and dt!=at;within=agreed and a0r[dt]<=maximum_movement+1;forced=dt if within else at;huge=1e6
123
+ cost=[[abs(position-e2r[i]) if abs(position-a0r[i])<=maximum_movement else huge for position in range(1,len(a0)+1)] for i in range(len(a0))]
124
+ for i in range(len(a0)):cost[i][0]=0. if i==forced else huge
125
+ cost[forced]=[0.]+[huge]*(len(a0)-1);rows,cols=linear_sum_assignment(cost);ranks=[0]*len(a0)
126
+ for row,col in zip(rows,cols):
127
+ if cost[int(row)][int(col)]>=huge:raise ContractError("source bounded assignment is infeasible")
128
+ ranks[int(row)]=int(col)+1
129
+ movement=max(abs(x-y) for x,y in zip(ranks,a0r))
130
+ return [-float(r) for r in ranks],{'forced_top_candidate':signals[forced]['candidate_id'],'agreed_alternative':agreed,'alternative_blocked_by_movement_cap':agreed and not within,'maximum_movement':movement}
131
+
132
+
133
+ def validate_statistical_parameters(method,parameters=None):
134
+ """Validate and detach method policy before any source model is invoked."""
135
+ if method not in METHOD_REGISTRY:raise ContractError("unknown statistical reranking method")
136
+ if parameters is not None and not isinstance(parameters,Mapping):raise ContractError('method parameters must be an object')
137
+ parameters=dict(parameters or {});allowed={'s5_minilm_weight','s5_lexical_weight','selection_digest'} if method in ('S5a','S5b','S5c') else set()
138
+ if set(parameters)-allowed:raise ContractError("unknown method parameter")
139
+ if method in ('S5a','S5b','S5c') and ('s5_minilm_weight' not in parameters or 'selection_digest' not in parameters):
140
+ raise ContractError("S5 requires its frozen development selection weight and digest")
141
+ if 'selection_digest' in parameters:
142
+ from .integration_contracts import require_digest
143
+ require_digest(parameters['selection_digest'],'selection_digest')
144
+ mw=finite(parameters.get('s5_minilm_weight',.55));lw=finite(parameters.get('s5_lexical_weight',.2))
145
+ if not 0<=mw<=1 or not 0<=lw<1:raise ContractError("S5 weights must be in range")
146
+ return parameters,mw,lw
147
+
148
+
149
+ def score_signals(signals,method,*,parameters=None):
150
+ """Return separately typed fields in original signal order; no grading."""
151
+ parameters,mw,lw=validate_statistical_parameters(method,parameters)
152
+ alias={'S6':'Jina-60/40','S6-E1':'Jina-E1','S6-E2':'Jina-E2','S6-E3':'Jina-E3','S6-E4':'Jina-E4','S7':'Jina-RRF'}
153
+ named=alias.get(method,method);stat=method.startswith('S');required=[]
154
+ if named not in ('S1','Dense-only') and not named.startswith('Jina'):
155
+ required.append('minilm_logit')
156
+ if named.startswith('Jina') or named=='Guarded-Jina-E2':required.append('jina_logit')
157
+ rows=validate_signals(signals,required);dense=[r['dense_score'] for r in rows];mini=[r.get('minilm_logit',0.) for r in rows];jina=[r.get('jina_logit',0.) for r in rows];diagnostics={}
158
+ if named in ('S1','Dense-only'):utility=dense
159
+ elif named in ('A0','A0-A','A0-B'):utility=_convex(dense,mini,.45)
160
+ elif named=='MiniLM-only':utility=mini
161
+ elif named=='Jina-only':utility=jina
162
+ elif named in ('S2','S4','MiniLM-robust-mean','MiniLM-robust-dominance'):utility=_convex(dense,mini,.5,robust=True)
163
+ elif named=='S3':
164
+ mr=_ranks(mini,rows);utility=[1/(60+r['dense_rank'])+1/(60+rank) for r,rank in zip(rows,mr)]
165
+ elif named in ('S5a','S5b','S5c'):
166
+ if named=='S5a':lw=0.
167
+ d,m=robust_normalize(dense),robust_normalize(mini);lex=candidate_pool_bm25(rows) if lw or named=='S5c' else [0.]*len(rows);l=robust_normalize(lex)
168
+ if named=='S5c':weights,confidence=adaptive_weights(rows,mw,lw);diagnostics={'weights':weights,'confidence':confidence}
169
+ else:weights={'dense':(1-lw)*(1-mw),'minilm':(1-lw)*mw,'lexical':lw};diagnostics={'weights':weights}
170
+ utility=[weights['dense']*x+weights['minilm']*y+weights['lexical']*z for x,y,z in zip(d,m,l)]
171
+ elif '-W' in named or named=='Jina-60/40':
172
+ weight=.6 if named=='Jina-60/40' else int(named.split('-W')[1].split('/')[0])/100;utility=_convex(dense,jina if named.startswith('Jina') else mini,weight)
173
+ elif named in ('MiniLM-RRF','Jina-RRF'):
174
+ w=.6 if named.startswith('Jina') else .5;dr=_ranks(dense,rows,pool_order=True);mr=_ranks(jina if named.startswith('Jina') else mini,rows,pool_order=True);utility=[w/(60+x)+(1-w)/(60+y) for x,y in zip(dr,mr)]
175
+ elif named=='Jina-E1':
176
+ parts=[_ranks(_convex(dense,jina,w),rows,pool_order=True) for w in E2_WEIGHTS];utility=[-mean([part[i] for part in parts]) for i in range(len(rows))]
177
+ elif named in E2_BANDS:utility=_e2(dense,jina,rows,E2_BANDS[named])
178
+ elif named=='Jina-E3':
179
+ parts=[_ranks(_convex(dense,jina,w),rows,pool_order=True) for w in E2_WEIGHTS];dr=_ranks(dense,rows,pool_order=True);order=sorted(range(len(rows)),key=lambda i:(percentile([p[i] for p in parts]),mean([p[i] for p in parts]),dr[i],rows[i]['candidate_id']));utility=[0.]*len(rows)
180
+ for pos,index in enumerate(order,1):utility[index]=-float(pos)
181
+ elif named in ('Jina-E4','Jina-E4-no-lexical'):
182
+ utility=_e2(dense,jina,rows,robust=True)
183
+ if named=='Jina-E4':
184
+ bm25=[r['bm25_score'] for r in rows] if all('bm25_score' in r for r in rows) else candidate_pool_bm25(rows);br=_ranks(bm25,rows,pool_order=True);utility=[.85*x+.15/(60+y) for x,y in zip(utility,br)]
185
+ elif named=='Guarded-Jina-E2':
186
+ utility=_convex(dense,mini,.45);ar=_ranks(utility,rows,pool_order=True);order=sorted(range(len(rows)),key=lambda i:ar[i]);margin=utility[order[0]]-utility[order[1]] if len(rows)>1 else float('inf');triggered=max(range(len(rows)),key=lambda i:dense[i])!=max(range(len(rows)),key=lambda i:mini[i]) and margin<=1.4397302163247685;diagnostics={'triggered':triggered,'trigger_threshold':1.4397302163247685,'rank_movement_cap':2,'latency_scope':'no inference observed by pure score fusion'}
187
+ if triggered:utility,merge=bounded_merge(utility,_e2(dense,jina,rows),dense,jina,rows);diagnostics.update(merge)
188
+ else:raise ContractError("method lacks a verified implementation")
189
+ reference='statistical.ts' if stat else 'cherry.py'
190
+ fields={'ranking_utility':{'kind':'utility','values':utility}}
191
+ if named=='A0':fields['raw_model_sigmoid']={'kind':'diagnostic','values':[sigmoid(max(-60.,min(60.,v))) for v in mini]}
192
+ elif named=='A0-A':fields['display_utility']={'kind':'utility','values':utility}
193
+ else:
194
+ display=[max(0.,min(1.,v)) for v in dense] if named in ('S1','Dense-only') else bounded_relative(utility) if named in ('S2','S3','MiniLM-robust-mean','MiniLM-RRF') else dominance(utility,reference=reference)
195
+ fields['pool_relative_strength']={'kind':'pool_relative','values':display}
196
+ if 'minilm_logit' in required:fields['raw_minilm_logit']={'kind':'utility','values':mini}
197
+ if 'jina_logit' in required:fields['raw_jina_logit']={'kind':'utility','values':jina}
198
+ if any(not math.isfinite(v) for field in fields.values() for v in field['values']):raise ContractError("nonfinite derived score")
199
+ return {'schema_version':'sphynx.rerank-family.scores.v1','method':method,'method_version':'v1','candidate_ids':[r['candidate_id'] for r in rows],'fields':fields,'diagnostics':diagnostics,'reference_sources':dict(SOURCE_PINS),'inference_observed':False}
200
+
201
+
202
+ class StatisticalScorer:
203
+ """A callable candidate composing pinned source scorers with the v1 I/O."""
204
+ def __init__(self,method,*,scorers=None,parameters=None):
205
+ if method not in METHOD_REGISTRY:raise ContractError("unknown statistical method")
206
+ checked,_,_=validate_statistical_parameters(method,parameters)
207
+ self.method,self.scorers,self.parameters=method,MappingProxyType(dict(scorers or {})),MappingProxyType(checked)
208
+ if set(self.scorers)-{'minilm','jina'}:raise ContractError("unknown source scorer")
209
+ self._configuration=family_configuration(method,STATISTICAL_IMPLEMENTATION_DIGEST,parameters=self.parameters,scorers=self.scorers)
210
+ @property
211
+ def configuration(self):return self._configuration
212
+ def score_fields(self,request):
213
+ from .model_adapters import request_pairs
214
+ request_pairs(request)
215
+ frozen=self.configuration.to_dict()['parameters']
216
+ if self.method!=frozen['method'] or dict(self.parameters)!=frozen['parameters']:raise ContractError("frozen family configuration changed")
217
+ signals=[{'candidate_id':item['candidate_id'],'dense_score':item['dense_score'],'dense_rank':item['dense_rank'],'query_text':request['query'],'passage_text':item['input']} for item in request['candidates']]
218
+ for name,scorer in self.scorers.items():
219
+ if scorer.configuration.to_dict()!=self.configuration.to_dict()['parameters']['source_scorers'][name]:raise ContractError("source scorer configuration changed")
220
+ response=scorer(request)
221
+ if response.get('query_id')!=request['query_id'] or response.get('pool_digest')!=request['pool_digest'] or response.get('schema_version')!='sphynx.rag.rerank.response.v1':raise ContractError("source scorer response identity differs")
222
+ scores=response.get('scores');values={item.get('candidate_id'):item.get('ranking_score') for item in scores} if isinstance(scores,list) and all(isinstance(x,dict) for x in scores) else {}
223
+ if len(values)!=len(signals) or len(scores)!=len(signals) or set(values)!={r['candidate_id'] for r in signals}:raise ContractError("source scorer response membership differs")
224
+ for signal in signals:signal[name+'_logit']=finite(values[signal['candidate_id']])
225
+ return score_signals(signals,self.method,parameters=self.parameters)
226
+ def __call__(self,request):
227
+ result=self.score_fields(request)
228
+ return {'schema_version':'sphynx.rag.rerank.response.v1','query_id':request['query_id'],'pool_digest':request['pool_digest'],'scores':[{'candidate_id':cid,'ranking_score':score} for cid,score in zip(result['candidate_ids'],result['fields']['ranking_utility']['values'])]}
229
+
230
+
231
+ def create_statistical_scorer(method,*,scorers=None,parameters=None):return StatisticalScorer(method,scorers=scorers,parameters=parameters)
232
+ STATISTICAL_IMPLEMENTATION_DIGEST=value_digest({p.name:'sha256:'+hashlib.sha256(p.read_bytes()).hexdigest() for p in (Path(__file__),Path(__file__).with_name('rerank_family_contracts.py'))})