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,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'))})
|