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,551 @@
|
|
|
1
|
+
"""Common workflow adapter for native source-bound RAG pipeline execution.
|
|
2
|
+
|
|
3
|
+
The existing v1 runner, artifacts, references, grader and comparison remain the
|
|
4
|
+
authority. This adapter supports explicit manifest-bound replacements and reads
|
|
5
|
+
permitted stage projections. Its native attempts are a fixed grid, not retries.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from .assessment_contracts import Assessment, CaseView, EvidenceView
|
|
15
|
+
from .components import (
|
|
16
|
+
AdapterDescriptor, BatchConfiguration, ComponentManifest, ComponentRef, ComponentRegistry,
|
|
17
|
+
EvaluationTarget, OPERATIONS_BY_KIND, PIPELINE_COMPONENT_ORDER,
|
|
18
|
+
adapter_component_identity_sha256, manifest_validate_selected_models,
|
|
19
|
+
)
|
|
20
|
+
from .dataset_v3 import load_dataset_v3
|
|
21
|
+
from .integration_contracts import CandidateConfiguration, TrackEvaluationRef, TrackRunRef, value_digest
|
|
22
|
+
from .pipeline import (
|
|
23
|
+
GRADER_RELEASE, INTERFACE, METRIC_RELEASE, STAGES, _policy, _score, _verified,
|
|
24
|
+
build_pipeline_request, _compare_report_data, grade_pipeline, report_pipeline,
|
|
25
|
+
run_pipeline, validate_pipeline_response,
|
|
26
|
+
)
|
|
27
|
+
from ._composed_pipeline import compose_pipeline
|
|
28
|
+
from ._pipeline_core import CHANGE_COMPONENTS, _flatten
|
|
29
|
+
from ._track_capture import read_artifact, verify_track_evaluation
|
|
30
|
+
from .chunking import _documents, _select_tasks, _verify_approval
|
|
31
|
+
from .stage_contracts import (
|
|
32
|
+
STAGE_EVIDENCE_CAPABILITY, StageContractError, StageDefinition, StageEvidence,
|
|
33
|
+
StageReplacement, StageSelector,
|
|
34
|
+
)
|
|
35
|
+
from .workflow_contracts import (
|
|
36
|
+
AdmissionBinding, BackendDescriptor, Criterion, Dataset, DatasetDefinition,
|
|
37
|
+
EvaluatorDefinition, MetricSpec, PayloadSchema, ReferenceBinding, ResolvedBinding,
|
|
38
|
+
SuiteDefinition, WorkflowContractError, _text, _thaw, contract_digest,
|
|
39
|
+
)
|
|
40
|
+
from .workflow_registry import WorkflowRegistry
|
|
41
|
+
|
|
42
|
+
_KINDS = PIPELINE_COMPONENT_ORDER
|
|
43
|
+
_GROUPS = dict(zip(_KINDS, ("chunking", "embedding", "retrieval", "reranker", "fusion", "dedup", "gating")))
|
|
44
|
+
_TARGET = EvaluationTarget("level2", "pipeline")
|
|
45
|
+
_CASE_SCHEMA = "sphynx.pipeline.case-input.v1"
|
|
46
|
+
_ROW_FIELDS = {
|
|
47
|
+
"candidate_id", "chunk_id", "document_id", "start", "end", "text", "input", "input_sha256",
|
|
48
|
+
"embedding", "dense_rank", "dense_score", "reranker_rank", "reranker_score",
|
|
49
|
+
"fusion_rank", "fusion_score", "dedup_decision", "post_dedup_rank",
|
|
50
|
+
"gate_decision", "gate_threshold", "final_rank",
|
|
51
|
+
}
|
|
52
|
+
_METRICS = tuple(f"sphynx.pipeline.{stage}.{metric}" for stage in STAGES
|
|
53
|
+
for metric in ("candidate_count", "recall_at_5", "ndcg_at_5", "mrr"))
|
|
54
|
+
_EVALUATOR = "sphynx.pipeline.native-grader.v1"
|
|
55
|
+
_AGGREGATION = "sphynx.pipeline.native-aggregation.v1"
|
|
56
|
+
_COMPARISON = "sphynx.pipeline.native-comparison.v1"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def stage_schema_id(kind, direction):
|
|
60
|
+
return f"sphynx.pipeline.{kind}.{direction}.v1"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _rows(value, name):
|
|
64
|
+
if type(value) is not list or any(type(row) is not dict for row in value):
|
|
65
|
+
raise WorkflowContractError(name + " must contain JSON objects")
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _validate_case(value):
|
|
70
|
+
if type(value) is not dict or set(value) != {"case_id", "query", "conversation_context", "documents"}:
|
|
71
|
+
raise WorkflowContractError("pipeline case input fields differ")
|
|
72
|
+
_text(value["case_id"], "case_id")
|
|
73
|
+
_text(value["query"], "query")
|
|
74
|
+
_rows(value["documents"], "documents")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def validate_stage_input(kind, value):
|
|
78
|
+
if kind not in _KINDS or type(value) is not dict:
|
|
79
|
+
raise WorkflowContractError("unsupported pipeline stage input")
|
|
80
|
+
expected = ({"task_id", "documents"} if kind == "chunker" else {"items"} if kind == "embedder"
|
|
81
|
+
else {"task_id", "query", "query_embedding", "candidates"}
|
|
82
|
+
if kind in {"retriever", "reranker", "fusion"} else {"task_id", "query", "candidates"})
|
|
83
|
+
if set(value) != expected:
|
|
84
|
+
raise WorkflowContractError("pipeline stage input fields differ")
|
|
85
|
+
if "task_id" in value:
|
|
86
|
+
_text(value["task_id"], "task_id")
|
|
87
|
+
if "query" in value:
|
|
88
|
+
_text(value["query"], "query")
|
|
89
|
+
if kind == "chunker":
|
|
90
|
+
for row in _rows(value["documents"], "documents"):
|
|
91
|
+
if not {"document_id", "text"} <= set(row) or not set(row) <= {
|
|
92
|
+
"document_id", "text", "content_sha256", "document_revision_family_id"}:
|
|
93
|
+
raise WorkflowContractError("pipeline document input fields differ")
|
|
94
|
+
_text(row["document_id"], "document_id")
|
|
95
|
+
_text(row["text"], "document text")
|
|
96
|
+
else:
|
|
97
|
+
for row in _rows(value["items" if kind == "embedder" else "candidates"], "stage rows"):
|
|
98
|
+
if "candidate_id" not in row or not set(row) <= _ROW_FIELDS:
|
|
99
|
+
raise WorkflowContractError("pipeline candidate input fields differ")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def validate_stage_output(kind, value):
|
|
103
|
+
if kind not in _KINDS or type(value) is not dict:
|
|
104
|
+
raise WorkflowContractError("unsupported pipeline stage output")
|
|
105
|
+
key = "chunks" if kind == "chunker" else "embeddings" if kind == "embedder" else "candidates"
|
|
106
|
+
allowed = {key, "latency_ms"} | ({"selected_candidate_ids"} if kind == "gate" else set())
|
|
107
|
+
if key not in value or not set(value) <= allowed:
|
|
108
|
+
raise WorkflowContractError("pipeline stage output fields differ")
|
|
109
|
+
for row in _rows(value[key], "stage output rows"):
|
|
110
|
+
fields = {"candidate_id", "vector"} if kind == "embedder" else _ROW_FIELDS
|
|
111
|
+
if not set(row) <= fields:
|
|
112
|
+
raise WorkflowContractError("pipeline stage output has unapproved fields")
|
|
113
|
+
# PayloadSchema additionally checks finite JSON and immutable content.
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _stage_definition(component):
|
|
117
|
+
return StageDefinition(
|
|
118
|
+
component.kind, component.implementation.implementation_id, component.version,
|
|
119
|
+
stage_schema_id(component.kind, "input"), stage_schema_id(component.kind, "output"),
|
|
120
|
+
"sha256:" + component.implementation.artifact_sha256, "sha256:" + component.config_sha256,
|
|
121
|
+
metadata={"component_id": component.component_id,
|
|
122
|
+
"descriptor_digest": "sha256:" + adapter_component_identity_sha256(
|
|
123
|
+
AdapterDescriptor.from_component(component))},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass(frozen=True)
|
|
128
|
+
class PipelineSource:
|
|
129
|
+
"""Operator-provided metadata; constructing it does not read dataset bytes."""
|
|
130
|
+
|
|
131
|
+
path: Path = field(repr=False, compare=False)
|
|
132
|
+
sha256: str
|
|
133
|
+
dataset_id: str
|
|
134
|
+
version: str
|
|
135
|
+
case_ids: tuple[str, ...]
|
|
136
|
+
|
|
137
|
+
def __post_init__(self):
|
|
138
|
+
object.__setattr__(self, "path", Path(self.path))
|
|
139
|
+
for name in ("dataset_id", "version"):
|
|
140
|
+
_text(getattr(self, name), name)
|
|
141
|
+
from .integration_contracts import require_digest
|
|
142
|
+
require_digest(self.sha256, "dataset sha256")
|
|
143
|
+
if isinstance(self.case_ids, (str, bytes)) or not self.case_ids:
|
|
144
|
+
raise WorkflowContractError("pipeline source needs an ordered case cohort")
|
|
145
|
+
ids = tuple(_text(value, "case_id") for value in self.case_ids)
|
|
146
|
+
if len(ids) != len(set(ids)):
|
|
147
|
+
raise WorkflowContractError("pipeline source has duplicate cases")
|
|
148
|
+
object.__setattr__(self, "case_ids", ids)
|
|
149
|
+
|
|
150
|
+
def to_dict(self):
|
|
151
|
+
return {"sha256": self.sha256, "dataset_id": self.dataset_id,
|
|
152
|
+
"version": self.version, "case_ids": list(self.case_ids)}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass(frozen=True)
|
|
156
|
+
class PipelineCandidate:
|
|
157
|
+
"""Pinned component configuration and caller-declared revision; not attestation."""
|
|
158
|
+
|
|
159
|
+
configuration: BatchConfiguration
|
|
160
|
+
revision: str
|
|
161
|
+
replacement: StageReplacement | None = None
|
|
162
|
+
|
|
163
|
+
def __post_init__(self):
|
|
164
|
+
if not isinstance(self.configuration, BatchConfiguration):
|
|
165
|
+
raise WorkflowContractError("pipeline candidate needs a BatchConfiguration")
|
|
166
|
+
_text(self.revision, "candidate revision")
|
|
167
|
+
if self.replacement is not None and not isinstance(self.replacement, StageReplacement):
|
|
168
|
+
raise WorkflowContractError("pipeline replacement must be StageReplacement")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class PipelineReport:
|
|
172
|
+
def __init__(self, data):
|
|
173
|
+
self._data = json.loads(json.dumps(data, ensure_ascii=False, allow_nan=False))
|
|
174
|
+
|
|
175
|
+
def to_dict(self):
|
|
176
|
+
return json.loads(json.dumps(self._data, ensure_ascii=False))
|
|
177
|
+
|
|
178
|
+
def to_json(self):
|
|
179
|
+
return json.dumps(self._data, ensure_ascii=False, sort_keys=True, allow_nan=False)
|
|
180
|
+
|
|
181
|
+
def to_markdown(self):
|
|
182
|
+
metrics = self._data["metrics"]
|
|
183
|
+
lines = ["# Pipeline report", "", f"Grading pass: {self._data['evaluation_id']}", "",
|
|
184
|
+
f"Coverage: {metrics['coverage']}", "", "| Stage | nDCG@5 | Recall@5 | MRR |",
|
|
185
|
+
"| --- | --- | --- | --- |"]
|
|
186
|
+
for stage, values in metrics["stage_metrics"].items():
|
|
187
|
+
lines.append("| " + stage + " | " + " | ".join(
|
|
188
|
+
"unavailable" if values[key] is None else str(values[key])
|
|
189
|
+
for key in ("ndcg_at_5", "recall_at_5", "mrr")) + " |")
|
|
190
|
+
return "\n".join(lines)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PipelineBackend:
|
|
194
|
+
"""Native fixed-grid subsystem execution with explicit component replacement."""
|
|
195
|
+
|
|
196
|
+
def __init__(self, source: PipelineSource, component_manifest: ComponentManifest,
|
|
197
|
+
baseline_configuration: BatchConfiguration, component_registry: ComponentRegistry, *,
|
|
198
|
+
suite_id="rag.pipeline.v1", synthetic=False, owner_authorized=False,
|
|
199
|
+
pack_manifest_path=None, allowed_implementation_kinds=("builtin", "recorded"),
|
|
200
|
+
trials=1, attempts=1):
|
|
201
|
+
if not isinstance(source, PipelineSource) or not isinstance(component_manifest, ComponentManifest):
|
|
202
|
+
raise WorkflowContractError("pipeline backend requires source and component manifest")
|
|
203
|
+
if not isinstance(component_registry, ComponentRegistry) or component_registry.manifest.sha256 != component_manifest.sha256:
|
|
204
|
+
raise WorkflowContractError("pipeline registry differs from its manifest")
|
|
205
|
+
if type(synthetic) is not bool or type(owner_authorized) is not bool:
|
|
206
|
+
raise WorkflowContractError("pipeline authorization flags must be boolean")
|
|
207
|
+
for number in (trials, attempts):
|
|
208
|
+
if type(number) is not int or number < 1:
|
|
209
|
+
raise WorkflowContractError("native fixed-grid counts must be positive integers")
|
|
210
|
+
_text(suite_id, "suite_id")
|
|
211
|
+
self.source, self.components, self.baseline, self.adapters = source, component_manifest, baseline_configuration, component_registry
|
|
212
|
+
self.synthetic, self.owner_authorized = synthetic, owner_authorized
|
|
213
|
+
self.pack_manifest_path = None if pack_manifest_path is None else Path(pack_manifest_path)
|
|
214
|
+
self.allowed_implementation_kinds = tuple(allowed_implementation_kinds)
|
|
215
|
+
if (not self.allowed_implementation_kinds
|
|
216
|
+
or len(set(self.allowed_implementation_kinds)) != len(self.allowed_implementation_kinds)
|
|
217
|
+
or set(self.allowed_implementation_kinds) - {"builtin", "recorded", "command"}):
|
|
218
|
+
raise WorkflowContractError("pipeline implementation permissions must be explicit supported kinds")
|
|
219
|
+
self.trials, self.attempts, self.suite_id = trials, attempts, suite_id
|
|
220
|
+
self._configuration(baseline_configuration)
|
|
221
|
+
metadata = {"source": source.to_dict(), "component_manifest_digest": "sha256:" + component_manifest.sha256,
|
|
222
|
+
"baseline": self._configuration(baseline_configuration), "synthetic": synthetic,
|
|
223
|
+
"owner_authorized": owner_authorized, "pack_manifest_provided": pack_manifest_path is not None,
|
|
224
|
+
"fixed_grid": {"trials": trials, "attempts": attempts, "selection": "first_success_per_trial"},
|
|
225
|
+
"allowed_implementation_kinds": list(self.allowed_implementation_kinds)}
|
|
226
|
+
self._metadata_digest = contract_digest(metadata)
|
|
227
|
+
self._descriptor = BackendDescriptor(
|
|
228
|
+
"sphynx.pipeline." + suite_id, "v1", "sphynx.pipeline.native-workflow.v1",
|
|
229
|
+
("sphynx.pipeline.substitution.v1", STAGE_EVIDENCE_CAPABILITY, "sphynx.saved-assessment.v1"),
|
|
230
|
+
("subsystem",), ("local",), (suite_id,), metadata)
|
|
231
|
+
dataset = DatasetDefinition(
|
|
232
|
+
source.dataset_id, source.version, suite_id + ".provider", _CASE_SCHEMA,
|
|
233
|
+
selection={"case_ids": list(source.case_ids), "order": "native_dataset_order"},
|
|
234
|
+
identity={"sha256": source.sha256, "reference_access": "backend_authorized_native_grader_only"})
|
|
235
|
+
self._definition = SuiteDefinition(
|
|
236
|
+
suite_id, "v1", self.descriptor.backend_id, "Native source-bound pipeline",
|
|
237
|
+
configuration=metadata, criterion_ids=_METRICS, metric_ids=_METRICS,
|
|
238
|
+
evaluator_ids=(_EVALUATOR,), required_capabilities=("sphynx.pipeline.substitution.v1",),
|
|
239
|
+
datasets=(dataset,), metadata={"trust_tier": "local_unverified", "candidate_source_bytes_verified": False})
|
|
240
|
+
self._boundary = contract_digest({"suite": self._definition.to_dict(), "boundary": "composed_source_documents"})
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def descriptor(self):
|
|
244
|
+
self._metadata_unchanged()
|
|
245
|
+
return self._descriptor
|
|
246
|
+
|
|
247
|
+
def _metadata_unchanged(self):
|
|
248
|
+
current = {"source": self.source.to_dict(), "component_manifest_digest": "sha256:" + self.components.sha256,
|
|
249
|
+
"baseline": self._configuration(self.baseline), "synthetic": self.synthetic,
|
|
250
|
+
"owner_authorized": self.owner_authorized, "pack_manifest_provided": self.pack_manifest_path is not None,
|
|
251
|
+
"fixed_grid": {"trials": self.trials, "attempts": self.attempts, "selection": "first_success_per_trial"},
|
|
252
|
+
"allowed_implementation_kinds": list(self.allowed_implementation_kinds)}
|
|
253
|
+
if contract_digest(current) != self._metadata_digest:
|
|
254
|
+
raise WorkflowContractError("pipeline backend policy/identity drift")
|
|
255
|
+
|
|
256
|
+
def describe(self, suite_id):
|
|
257
|
+
if suite_id != self.suite_id:
|
|
258
|
+
raise WorkflowContractError("pipeline suite is not registered by this backend")
|
|
259
|
+
return self._definition
|
|
260
|
+
|
|
261
|
+
def _configuration(self, configuration):
|
|
262
|
+
if not isinstance(configuration, BatchConfiguration) or set(configuration.by_kind) != set(_KINDS):
|
|
263
|
+
raise WorkflowContractError("pipeline configuration requires exactly seven component kinds")
|
|
264
|
+
selected = {}
|
|
265
|
+
raw = {item["component_id"]: item for item in self.components.value["components"]}
|
|
266
|
+
for ref in configuration.components:
|
|
267
|
+
component = self.components.by_id.get(ref.component_id)
|
|
268
|
+
if component is None or ComponentRef(component.kind, component.component_id, component.version, component.config_sha256) != ref:
|
|
269
|
+
raise WorkflowContractError("pipeline component reference differs from manifest")
|
|
270
|
+
if _TARGET not in component.capabilities:
|
|
271
|
+
raise WorkflowContractError("pipeline component lacks subsystem capability")
|
|
272
|
+
selected[ref.kind] = raw[ref.component_id]
|
|
273
|
+
manifest_validate_selected_models((configuration,), self.components.by_id)
|
|
274
|
+
return selected
|
|
275
|
+
|
|
276
|
+
def baseline_candidate(self, *, revision):
|
|
277
|
+
return PipelineCandidate(self.baseline, revision)
|
|
278
|
+
|
|
279
|
+
def replace(self, stage_id, component_id, *, configuration_id, revision):
|
|
280
|
+
if stage_id not in _KINDS:
|
|
281
|
+
raise StageContractError("unknown native pipeline stage")
|
|
282
|
+
component = self.components.by_id.get(component_id)
|
|
283
|
+
if component is None or component.kind != stage_id:
|
|
284
|
+
raise StageContractError("replacement component has an incompatible kind")
|
|
285
|
+
before = self.components.by_id[self.baseline.by_kind[stage_id].component_id]
|
|
286
|
+
change = StageReplacement(stage_id, _stage_definition(before), _stage_definition(component))
|
|
287
|
+
refs = tuple(ComponentRef(component.kind, component.component_id, component.version, component.config_sha256)
|
|
288
|
+
if ref.kind == stage_id else ref for ref in self.baseline.components)
|
|
289
|
+
configuration = BatchConfiguration(configuration_id, refs)
|
|
290
|
+
self._configuration(configuration)
|
|
291
|
+
return PipelineCandidate(configuration, revision, change)
|
|
292
|
+
|
|
293
|
+
def _candidate(self, candidate):
|
|
294
|
+
if not isinstance(candidate, PipelineCandidate):
|
|
295
|
+
raise WorkflowContractError("pipeline candidate must be PipelineCandidate")
|
|
296
|
+
selected = self._configuration(candidate.configuration)
|
|
297
|
+
changes = [kind for kind in _KINDS if self.baseline.by_kind[kind] != candidate.configuration.by_kind[kind]]
|
|
298
|
+
if changes:
|
|
299
|
+
change = candidate.replacement
|
|
300
|
+
if change is None or changes != [change.stage_id]:
|
|
301
|
+
raise StageContractError("candidate changes differ from the explicit replacement")
|
|
302
|
+
change.require_baseline(_stage_definition(self.components.by_id[self.baseline.by_kind[change.stage_id].component_id]))
|
|
303
|
+
if change.replacement != _stage_definition(self.components.by_id[candidate.configuration.by_kind[change.stage_id].component_id]):
|
|
304
|
+
raise StageContractError("replacement component identity drift")
|
|
305
|
+
elif candidate.replacement is not None:
|
|
306
|
+
raise StageContractError("declared replacement did not occur")
|
|
307
|
+
controlled = {_GROUPS[kind]: selected[kind] for kind in _KINDS}
|
|
308
|
+
controlled["top_k"] = self.components.by_id[candidate.configuration.by_kind["gate"].component_id].config.get("top_k", 5)
|
|
309
|
+
return CandidateConfiguration(
|
|
310
|
+
candidate.configuration.configuration_id, INTERFACE, value_digest(selected),
|
|
311
|
+
{"controlled_config": controlled, "workflow_binding": self._definition.definition_digest,
|
|
312
|
+
"candidate_revision": candidate.revision,
|
|
313
|
+
"replacement": None if candidate.replacement is None else candidate.replacement.to_dict()}, "trusted_local")
|
|
314
|
+
|
|
315
|
+
def _authorize(self):
|
|
316
|
+
self._metadata_unchanged()
|
|
317
|
+
if not self.synthetic and (not self.owner_authorized or self.pack_manifest_path is None):
|
|
318
|
+
raise PermissionError("pipeline source/reference access requires explicit owner authorization and pack manifest")
|
|
319
|
+
|
|
320
|
+
def _loaded(self):
|
|
321
|
+
self._authorize()
|
|
322
|
+
dataset = load_dataset_v3(self.source.path, expected_sha256=self.source.sha256)
|
|
323
|
+
if (dataset.dataset_id, dataset.version) != (self.source.dataset_id, self.source.version):
|
|
324
|
+
raise WorkflowContractError("pipeline source metadata differs from verified dataset")
|
|
325
|
+
tasks = _select_tasks(dataset, self.source.case_ids)
|
|
326
|
+
if tuple(task["task_id"] for task in tasks) != self.source.case_ids:
|
|
327
|
+
raise WorkflowContractError("pipeline ordered case cohort differs")
|
|
328
|
+
return dataset, tasks
|
|
329
|
+
|
|
330
|
+
def cases(self, suite):
|
|
331
|
+
if suite.definition_digest != self._definition.definition_digest:
|
|
332
|
+
raise WorkflowContractError("pipeline suite identity drift")
|
|
333
|
+
_, tasks = self._loaded()
|
|
334
|
+
schema = PayloadSchema(_CASE_SCHEMA, "v1", "sphynx.pipeline.case-validator.v1", _validate_case)
|
|
335
|
+
return tuple(CaseView.validated(task["task_id"],
|
|
336
|
+
{"case_id": task["task_id"], "query": task["query"], "conversation_context": task["conversation_context"],
|
|
337
|
+
"documents": _documents(task)}, schema=schema) for task in tasks)
|
|
338
|
+
|
|
339
|
+
def _selected(self, plan):
|
|
340
|
+
if plan.suite.definition_digest != self._definition.definition_digest or plan.scope != "subsystem" or plan.mode != "local":
|
|
341
|
+
raise WorkflowContractError("pipeline requires its registered local subsystem suite")
|
|
342
|
+
if plan.parameters or plan.runtime:
|
|
343
|
+
raise WorkflowContractError("legacy pipeline controls are fixed in backend metadata; runtime overrides unsupported")
|
|
344
|
+
return self._candidate(plan.candidate)
|
|
345
|
+
|
|
346
|
+
def resolve(self, plan):
|
|
347
|
+
candidate = self._selected(plan)
|
|
348
|
+
return ResolvedBinding({"backend": self.descriptor.to_dict(), "suite": self._definition.to_dict(),
|
|
349
|
+
"candidate": candidate.to_dict(), "scope": plan.scope, "mode": plan.mode},
|
|
350
|
+
native_ref=candidate)
|
|
351
|
+
|
|
352
|
+
def admit(self, plan, resolved):
|
|
353
|
+
expected = self.resolve(plan)
|
|
354
|
+
if resolved.resolution_digest != expected.resolution_digest:
|
|
355
|
+
raise WorkflowContractError("pipeline resolution identity drift")
|
|
356
|
+
dataset, tasks = self._loaded()
|
|
357
|
+
candidate = self._selected(plan)
|
|
358
|
+
policy = _policy(dataset, self.source.sha256, candidate, boundary="composed_source_documents",
|
|
359
|
+
synthetic=self.synthetic, owner_authorized=self.owner_authorized,
|
|
360
|
+
pack_manifest_path=self.pack_manifest_path, trials=self.trials,
|
|
361
|
+
attempts=self.attempts, boundary_digest=self._boundary)
|
|
362
|
+
observed = {"approved_manifest_source": None if self.synthetic else self.pack_manifest_path.read_text(encoding="utf-8")}
|
|
363
|
+
_verify_approval(dataset, self.source.sha256, policy, observed)
|
|
364
|
+
descriptors = []
|
|
365
|
+
for ref in plan.candidate.configuration.components:
|
|
366
|
+
component = self.components.by_id[ref.component_id]
|
|
367
|
+
if component.implementation.kind not in self.allowed_implementation_kinds:
|
|
368
|
+
raise PermissionError("pipeline component implementation kind is not authorized")
|
|
369
|
+
adapter = self.adapters.resolve(ref.component_id, _TARGET, OPERATIONS_BY_KIND[ref.kind][0])
|
|
370
|
+
if adapter.descriptor != AdapterDescriptor.from_component(component):
|
|
371
|
+
raise WorkflowContractError("pipeline adapter identity drift")
|
|
372
|
+
descriptors.append(_stage_definition(component).to_dict())
|
|
373
|
+
return AdmissionBinding(resolved.resolution_digest,
|
|
374
|
+
{"dataset_digest": self.source.sha256, "case_ids": [task["task_id"] for task in tasks],
|
|
375
|
+
"stages": descriptors, "policy_digest": value_digest(policy)}, "local_unverified", True, False)
|
|
376
|
+
|
|
377
|
+
def recheck(self, plan, resolved, admission):
|
|
378
|
+
if admission.to_dict() != self.admit(plan, resolved).to_dict():
|
|
379
|
+
raise WorkflowContractError("pipeline admission facts changed")
|
|
380
|
+
|
|
381
|
+
def execute(self, plan, resolved, admission, output_dir):
|
|
382
|
+
self.recheck(plan, resolved, admission)
|
|
383
|
+
if output_dir is None:
|
|
384
|
+
raise WorkflowContractError("native pipeline requires an output directory")
|
|
385
|
+
def invoke(kind, operation, payload):
|
|
386
|
+
validate_stage_input(kind, payload)
|
|
387
|
+
ref = plan.candidate.configuration.by_kind[kind]
|
|
388
|
+
output = self.adapters.resolve(ref.component_id, _TARGET, operation).invoke(operation, payload)
|
|
389
|
+
validate_stage_output(kind, output)
|
|
390
|
+
return output
|
|
391
|
+
return run_pipeline(self._selected(plan), lambda request: compose_pipeline(request, invoke),
|
|
392
|
+
self.source.path, output_dir, expected_sha256=self.source.sha256,
|
|
393
|
+
synthetic=self.synthetic, owner_authorized=self.owner_authorized,
|
|
394
|
+
pack_manifest_path=self.pack_manifest_path, trials=self.trials, attempts=self.attempts,
|
|
395
|
+
case_ids=self.source.case_ids, boundary_digest=self._boundary, boundary="composed_source_documents")
|
|
396
|
+
|
|
397
|
+
def accepts_reference(self, native_ref, kind):
|
|
398
|
+
return isinstance(native_ref, TrackRunRef if kind == "run" else TrackEvaluationRef) if kind in {"run", "grading"} else False
|
|
399
|
+
|
|
400
|
+
def _run(self, native_ref):
|
|
401
|
+
self._authorize()
|
|
402
|
+
run = native_ref.run if isinstance(native_ref, TrackEvaluationRef) else native_ref
|
|
403
|
+
if not isinstance(run, TrackRunRef):
|
|
404
|
+
raise WorkflowContractError("pipeline needs a native TrackRunRef")
|
|
405
|
+
manifest, tasks, policy, requests = _verified(run, self.source.path, self.source.sha256)
|
|
406
|
+
if (manifest.identity.subject_digest != self._boundary
|
|
407
|
+
or manifest.candidate.parameters.get("workflow_binding") != self._definition.definition_digest
|
|
408
|
+
or manifest.case_ids != self.source.case_ids):
|
|
409
|
+
raise WorkflowContractError("native run belongs to a different pipeline binding")
|
|
410
|
+
return run, manifest, tasks, policy, requests
|
|
411
|
+
|
|
412
|
+
def reference(self, native_ref, kind):
|
|
413
|
+
if not self.accepts_reference(native_ref, kind):
|
|
414
|
+
raise WorkflowContractError("unsupported native pipeline reference")
|
|
415
|
+
run, manifest, _, _, _ = self._run(native_ref)
|
|
416
|
+
if kind == "grading":
|
|
417
|
+
verified = verify_track_evaluation(native_ref)
|
|
418
|
+
return ReferenceBinding(verified.evaluation_id, "graded", "local_unverified", run.run_id)
|
|
419
|
+
status = "completed_with_failures" if any(row.failure for row in manifest.captures) else "completed"
|
|
420
|
+
return ReferenceBinding(run.run_id, status, "local_unverified", run.run_id)
|
|
421
|
+
|
|
422
|
+
def assess(self, native_run):
|
|
423
|
+
self._run(native_run)
|
|
424
|
+
return grade_pipeline(native_run, self.source.path, expected_sha256=self.source.sha256)
|
|
425
|
+
|
|
426
|
+
def report(self, native_grading):
|
|
427
|
+
self._run(native_grading)
|
|
428
|
+
return PipelineReport(report_pipeline(native_grading, self.source.path, expected_sha256=self.source.sha256))
|
|
429
|
+
|
|
430
|
+
def assessments(self, native_grading, case_id=None):
|
|
431
|
+
run, manifest, tasks, _, requests = self._run(native_grading)
|
|
432
|
+
data = self.report(native_grading).to_dict()
|
|
433
|
+
if case_id is not None and case_id not in manifest.case_ids:
|
|
434
|
+
raise StageContractError("case is not in this pipeline report")
|
|
435
|
+
result = []
|
|
436
|
+
by_id = {task["task_id"]: task for task in tasks}
|
|
437
|
+
# Per-attempt diagnostics retain failed attempts. The native report's
|
|
438
|
+
# aggregate still selects first_success_per_trial, without redefinition.
|
|
439
|
+
for capture in manifest.captures:
|
|
440
|
+
if case_id is not None and capture.case_id != case_id:
|
|
441
|
+
continue
|
|
442
|
+
score, missing = None, 0
|
|
443
|
+
if capture.failure is None and capture.response is not None:
|
|
444
|
+
score, missing = _score(by_id[capture.case_id], read_artifact(run.path, capture.response),
|
|
445
|
+
requests[capture.case_id])
|
|
446
|
+
for stage in STAGES:
|
|
447
|
+
for metric in ("candidate_count", "recall_at_5", "ndcg_at_5", "mrr"):
|
|
448
|
+
value = None if score is None else score["stage_metrics"][stage][metric]
|
|
449
|
+
availability = "available" if value is not None else "undefined" if score is not None else "missing"
|
|
450
|
+
reason = (None if value is not None else "native_metric_population_undefined" if score is not None
|
|
451
|
+
else "native_labels_missing" if missing else capture.failure or "native_response_missing")
|
|
452
|
+
result.append(Assessment(
|
|
453
|
+
f"sphynx.pipeline.{stage}.{metric}", _EVALUATOR, capture.case_id, "score",
|
|
454
|
+
availability, value=value, reason=reason,
|
|
455
|
+
evidence_ids=() if capture.response is None else (capture.response.sha256,),
|
|
456
|
+
run_id=run.run_id, grading_id=data["evaluation_id"], trial=capture.trial_index,
|
|
457
|
+
attempt=capture.attempt_index))
|
|
458
|
+
return tuple(result)
|
|
459
|
+
|
|
460
|
+
def compare(self, baseline, candidate):
|
|
461
|
+
return self._comparison_data(baseline, candidate)[1]
|
|
462
|
+
|
|
463
|
+
def _comparison_data(self, baseline, candidate):
|
|
464
|
+
self._run(baseline)
|
|
465
|
+
self._run(candidate)
|
|
466
|
+
reports = [self.report(ref).to_dict() for ref in (baseline, candidate)]
|
|
467
|
+
left, right = (report["metrics"]["controlled_config"] for report in reports)
|
|
468
|
+
a, b = _flatten(left), _flatten(right)
|
|
469
|
+
paths = tuple(sorted(key for key in a.keys() | b.keys() if a.get(key) != b.get(key)))
|
|
470
|
+
groups = {path.split(".", 1)[0] for path in paths}
|
|
471
|
+
if groups - CHANGE_COMPONENTS:
|
|
472
|
+
raise StageContractError("native pipeline comparison does not support changes: " + ", ".join(sorted(groups - CHANGE_COMPONENTS)))
|
|
473
|
+
return reports, _compare_report_data(*reports, declared_paths=paths)
|
|
474
|
+
|
|
475
|
+
def compare_view(self, baseline, candidate):
|
|
476
|
+
from ._comparison_adapters import _pipeline_view, _sources
|
|
477
|
+
reports, native = self._comparison_data(baseline, candidate)
|
|
478
|
+
conditions = [{"identity": report["identity"], "controlled_config": report["metrics"]["controlled_config"]}
|
|
479
|
+
for report in reports]
|
|
480
|
+
return _pipeline_view(_sources(self, baseline, candidate, conditions=conditions), native,
|
|
481
|
+
{identifier: _metric_spec(identifier) for identifier in _METRICS}, reports)
|
|
482
|
+
|
|
483
|
+
def stage_evidence(self, native_ref, selector):
|
|
484
|
+
if not isinstance(selector, StageSelector):
|
|
485
|
+
raise StageContractError("stage selection requires StageSelector")
|
|
486
|
+
if selector.stage_id not in _KINDS or selector.occurrence != 1:
|
|
487
|
+
raise StageContractError("native pipeline stage/occurrence is unsupported")
|
|
488
|
+
run, manifest, _, _, requests = self._run(native_ref)
|
|
489
|
+
if isinstance(native_ref, TrackEvaluationRef):
|
|
490
|
+
self.report(native_ref)
|
|
491
|
+
capture = next((row for row in manifest.captures if
|
|
492
|
+
(row.case_id, row.trial_index, row.attempt_index) ==
|
|
493
|
+
(selector.case_id, selector.trial, selector.attempt)), None)
|
|
494
|
+
if capture is None:
|
|
495
|
+
raise StageContractError("stage selector is outside this run's case/trial/attempt cohort")
|
|
496
|
+
available = capture.failure is None and capture.response is not None
|
|
497
|
+
evidence = EvidenceView(
|
|
498
|
+
f"{run.run_id}/{selector.case_id}/{selector.trial}/{selector.attempt}/{selector.stage_id}",
|
|
499
|
+
None if capture.response is None else capture.response.sha256, "application/json", "stage.output",
|
|
500
|
+
"native.pipeline.retained_stage_projection", "candidate_permitted",
|
|
501
|
+
schema_version="sphynx.rag.pipeline.composed.response.v1", case_id=selector.case_id,
|
|
502
|
+
step_id=selector.stage_id, availability="available" if available else "unavailable",
|
|
503
|
+
metadata={"artifact_path": None if capture.response is None else capture.response.path,
|
|
504
|
+
"projection_path": "/stages/" + selector.stage_id, "run_id": run.run_id,
|
|
505
|
+
"trial": selector.trial, "attempt": selector.attempt, "occurrence": 1,
|
|
506
|
+
"timing_authority": "component_reported_unattested"})
|
|
507
|
+
if not available:
|
|
508
|
+
return (StageEvidence.unavailable(selector, evidence,
|
|
509
|
+
reason="native_stage_output_not_retained:" + str(capture.failure)),)
|
|
510
|
+
response = validate_pipeline_response(read_artifact(run.path, capture.response), requests[selector.case_id])
|
|
511
|
+
projection = response["stages"][selector.stage_id]
|
|
512
|
+
schema = PayloadSchema(stage_schema_id(selector.stage_id, "output"), "v1",
|
|
513
|
+
"sphynx.pipeline.stage-output-validator.v1",
|
|
514
|
+
lambda value: validate_stage_output(selector.stage_id, value))
|
|
515
|
+
return (StageEvidence.projected(selector, evidence, projection, schema=schema),)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _metric_spec(identifier):
|
|
519
|
+
return MetricSpec(identifier, "v1", "count" if identifier.endswith("candidate_count") else "ratio",
|
|
520
|
+
"descriptive" if identifier.endswith("candidate_count") else "higher",
|
|
521
|
+
aggregation_policy=_AGGREGATION, comparison_policy=_COMPARISON,
|
|
522
|
+
population={"source": "native_per_metric_eligibility_missing_and_undefined"})
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def register_pipeline_backend(registry: WorkflowRegistry, backend: PipelineBackend):
|
|
526
|
+
"""Enable explicit operator bindings; registration loads no dataset or adapters."""
|
|
527
|
+
suite = backend.describe(backend.suite_id)
|
|
528
|
+
registry.register_backend(backend)
|
|
529
|
+
schema = registry.register_schema(PayloadSchema(_CASE_SCHEMA, "v1", "sphynx.pipeline.case-validator.v1", _validate_case))
|
|
530
|
+
registry.register_provider(Dataset(suite.datasets[0], schema, lambda selection: backend.cases(suite)))
|
|
531
|
+
for kind in _KINDS:
|
|
532
|
+
for direction, validator in (("input", validate_stage_input), ("output", validate_stage_output)):
|
|
533
|
+
registry.register_schema(PayloadSchema(
|
|
534
|
+
stage_schema_id(kind, direction), "v1", f"sphynx.pipeline.stage-{direction}-validator.v1",
|
|
535
|
+
lambda value, kind=kind, validator=validator: validator(kind, value)))
|
|
536
|
+
registry.register_policy(_AGGREGATION, version="v1", implementation_id=METRIC_RELEASE, kind="aggregation")
|
|
537
|
+
registry.register_policy(_COMPARISON, version="v1", implementation_id="sphynx.pipeline.compare.v1", kind="comparison")
|
|
538
|
+
for identifier in _METRICS:
|
|
539
|
+
registry.register_criterion(Criterion(identifier, "v1", identifier))
|
|
540
|
+
registry.register_metric(_metric_spec(identifier))
|
|
541
|
+
registry.register_evaluator(EvaluatorDefinition(_EVALUATOR, "v1", GRADER_RELEASE,
|
|
542
|
+
criterion_ids=_METRICS, metric_ids=_METRICS, required_capabilities=("sphynx.saved-assessment.v1",)))
|
|
543
|
+
registry.register_suite(suite)
|
|
544
|
+
return backend
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def build_pipeline_registry(backend: PipelineBackend) -> WorkflowRegistry:
|
|
548
|
+
"""Fresh registry with the operator's exact native dataset ID allowed."""
|
|
549
|
+
registry = WorkflowRegistry(legacy_ids=(backend.source.dataset_id,))
|
|
550
|
+
register_pipeline_backend(registry, backend)
|
|
551
|
+
return registry
|