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
sphynx/integrated_rag.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"""Versioned router/rewrite/retrieval/context/answer execution.
|
|
2
|
+
|
|
3
|
+
This boundary executes retrieval. It does not reinterpret the native supplied-
|
|
4
|
+
context v1 runner. Component calls are trusted local execution, not OS isolation.
|
|
5
|
+
Lifecycle journals retain stage artifacts, including partial work on failure.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from copy import deepcopy
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from .assessment_contracts import CaseView, _thaw
|
|
16
|
+
from .components import (
|
|
17
|
+
AdapterDescriptor, EvaluationTarget, OPERATIONS_BY_KIND, PIPELINE_COMPONENT_ORDER,
|
|
18
|
+
adapter_component_identity_sha256,
|
|
19
|
+
)
|
|
20
|
+
from .integration_contracts import value_digest
|
|
21
|
+
from .lifecycle import (
|
|
22
|
+
AttemptOutput, DriverDescriptor, DriverFailure, RunPolicy, execute_lifecycle,
|
|
23
|
+
)
|
|
24
|
+
from .pipeline import validate_pipeline_response
|
|
25
|
+
from .pipeline_workflow import (
|
|
26
|
+
PipelineBackend, PipelineCandidate, _stage_definition, validate_stage_input,
|
|
27
|
+
validate_stage_output,
|
|
28
|
+
)
|
|
29
|
+
from ._composed_pipeline import compose_pipeline, validate_chunks
|
|
30
|
+
from .workflow_contracts import PayloadSchema, PlanBinding, WorkflowContractError, _text, contract_digest
|
|
31
|
+
|
|
32
|
+
INTEGRATED_SCHEMA = "sphynx.rag.integrated.response.v1"
|
|
33
|
+
INTEGRATED_STAGE_SCHEMA = "sphynx.rag.integrated.stage.v1"
|
|
34
|
+
INTEGRATED_STAGES = ("router", "rewriter", *PIPELINE_COMPONENT_ORDER, "context", "generator")
|
|
35
|
+
_FULL_TARGET = EvaluationTarget("level3", "full_rag")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _require(value, message):
|
|
39
|
+
if not value:
|
|
40
|
+
raise WorkflowContractError(message)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def json_bytes(value):
|
|
44
|
+
return json.dumps(_thaw(value), ensure_ascii=False, allow_nan=False,
|
|
45
|
+
sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def text_digest(value):
|
|
49
|
+
return "sha256:" + hashlib.sha256(value.encode("utf-8")).hexdigest()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def validate_extra_output(kind, value):
|
|
53
|
+
_require(type(value) is dict, "integrated stage output must be an object")
|
|
54
|
+
if kind == "router":
|
|
55
|
+
_require(set(value) <= {"should_call_rag", "latency_ms"} and type(value.get("should_call_rag")) is bool,
|
|
56
|
+
"router must return a boolean should_call_rag")
|
|
57
|
+
elif kind == "rewriter":
|
|
58
|
+
_require("rewritten_query" in value and set(value) <= {"query", "rewritten_query", "latency_ms"},
|
|
59
|
+
"rewriter output fields differ")
|
|
60
|
+
_text(value["rewritten_query"], "rewritten query")
|
|
61
|
+
elif kind == "generator":
|
|
62
|
+
_require({"answer", "abstained"} <= set(value)
|
|
63
|
+
and set(value) <= {"answer", "abstained", "citations", "token_counts", "latency_ms"},
|
|
64
|
+
"generator output fields differ")
|
|
65
|
+
_require(type(value["answer"]) is str and type(value["abstained"]) is bool,
|
|
66
|
+
"generator answer/action differs")
|
|
67
|
+
_require(type(value.get("citations", [])) is list, "generator citations must be an array")
|
|
68
|
+
if "token_counts" in value:
|
|
69
|
+
counts = value["token_counts"]
|
|
70
|
+
_require(type(counts) is dict and set(counts) == {"prompt", "completion"}
|
|
71
|
+
and all(type(n) is int and n >= 0 for n in counts.values()), "token count fields differ")
|
|
72
|
+
elif kind == "context":
|
|
73
|
+
_require(type(value.get("rows")) is list and type(value.get("text")) is str
|
|
74
|
+
and type(value.get("bytes")) is int, "context output fields differ")
|
|
75
|
+
else:
|
|
76
|
+
raise WorkflowContractError("unknown integrated stage")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def build_context(response, documents, max_context_bytes):
|
|
80
|
+
"""Select whole source-bound chunks in final-rank order under a UTF-8 budget."""
|
|
81
|
+
_require(type(max_context_bytes) is int and max_context_bytes > 0, "context byte limit must be positive")
|
|
82
|
+
chunks = validate_chunks(response["construction"]["chunks"], documents)
|
|
83
|
+
source = {row["document_id"]: row for row in documents}
|
|
84
|
+
selected = sorted((row for row in response["candidates"] if row["final_rank"] is not None),
|
|
85
|
+
key=lambda row: row["final_rank"])
|
|
86
|
+
rows, texts, count = [], [], 0
|
|
87
|
+
for selected_row in selected:
|
|
88
|
+
chunk = chunks[selected_row["candidate_id"]]
|
|
89
|
+
text = chunk["input"]
|
|
90
|
+
extra = len(text.encode("utf-8")) + (2 if texts else 0)
|
|
91
|
+
if count + extra > max_context_bytes:
|
|
92
|
+
break
|
|
93
|
+
document = source[chunk["document_id"]]
|
|
94
|
+
_require(document.get("content_sha256") == text_digest(document["text"]).removeprefix("sha256:"),
|
|
95
|
+
"context source document digest differs")
|
|
96
|
+
rows.append({"candidate_id": chunk["candidate_id"], "document_id": chunk["document_id"],
|
|
97
|
+
"start": chunk["start"], "end": chunk["end"], "input": text,
|
|
98
|
+
"text_sha256": text_digest(text), "source_document_sha256": text_digest(document["text"])})
|
|
99
|
+
texts.append(text)
|
|
100
|
+
count += extra
|
|
101
|
+
text = "\n\n".join(texts)
|
|
102
|
+
return {"rows": rows, "text": text, "bytes": count, "text_sha256": text_digest(text),
|
|
103
|
+
"selection_policy": "whole_chunks_in_final_rank_order_utf8_bytes.v1"}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def validate_answer(output, context):
|
|
107
|
+
validate_extra_output("generator", output)
|
|
108
|
+
allowed = {row["candidate_id"] for row in context["rows"]}
|
|
109
|
+
ids = set()
|
|
110
|
+
for citation in output.get("citations", []):
|
|
111
|
+
_require(type(citation) is dict and set(citation) == {"citation_id", "evidence_id", "start", "end"},
|
|
112
|
+
"integrated citation fields differ")
|
|
113
|
+
_text(citation["citation_id"], "citation_id")
|
|
114
|
+
_require(citation["citation_id"] not in ids and citation["evidence_id"] in allowed,
|
|
115
|
+
"citation leaves the exact generator context")
|
|
116
|
+
ids.add(citation["citation_id"])
|
|
117
|
+
_require(type(citation["start"]) is int and type(citation["end"]) is int
|
|
118
|
+
and 0 <= citation["start"] < citation["end"] <= len(output["answer"]),
|
|
119
|
+
"citation offsets leave the answer")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class IntegratedCandidate:
|
|
124
|
+
pipeline: PipelineCandidate
|
|
125
|
+
router_id: str
|
|
126
|
+
rewriter_id: str
|
|
127
|
+
generator_id: str
|
|
128
|
+
revision: str
|
|
129
|
+
|
|
130
|
+
def __post_init__(self):
|
|
131
|
+
_require(isinstance(self.pipeline, PipelineCandidate), "integrated candidate needs PipelineCandidate")
|
|
132
|
+
for name in ("router_id", "rewriter_id", "generator_id", "revision"):
|
|
133
|
+
_text(getattr(self, name), name)
|
|
134
|
+
_require(self.revision == self.pipeline.revision, "component/subsystem/system candidate revisions differ")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class IntegratedDriver:
|
|
138
|
+
"""Cooperative lifecycle driver over explicitly registered native components."""
|
|
139
|
+
|
|
140
|
+
def __init__(self, pipeline: PipelineBackend, *, router_id, rewriter_id, generator_id,
|
|
141
|
+
max_context_bytes=16000):
|
|
142
|
+
_require(isinstance(pipeline, PipelineBackend), "integrated driver requires PipelineBackend")
|
|
143
|
+
_require(type(max_context_bytes) is int and max_context_bytes > 0, "context byte limit must be positive")
|
|
144
|
+
self.pipeline = pipeline
|
|
145
|
+
self.router_id, self.rewriter_id, self.generator_id = router_id, rewriter_id, generator_id
|
|
146
|
+
self.max_context_bytes = max_context_bytes
|
|
147
|
+
self._configuration = {"pipeline_binding": pipeline.describe(pipeline.suite_id).definition_digest,
|
|
148
|
+
"router_id": router_id, "rewriter_id": rewriter_id,
|
|
149
|
+
"generator_id": generator_id, "max_context_bytes": max_context_bytes}
|
|
150
|
+
self.descriptor = DriverDescriptor(
|
|
151
|
+
"sphynx.rag.integrated.driver", "sphynx.rag.integrated.driver.v1",
|
|
152
|
+
("sphynx.lifecycle.deadline.cooperative.v1",))
|
|
153
|
+
self._descriptor_digest = contract_digest(self.descriptor.to_dict())
|
|
154
|
+
for kind, identifier in (("router", router_id), ("rewriter", rewriter_id), ("generator", generator_id)):
|
|
155
|
+
self._component(kind, identifier, resolve=False)
|
|
156
|
+
|
|
157
|
+
def candidate(self, pipeline_candidate):
|
|
158
|
+
return IntegratedCandidate(pipeline_candidate, self.router_id, self.rewriter_id,
|
|
159
|
+
self.generator_id, pipeline_candidate.revision)
|
|
160
|
+
|
|
161
|
+
def _component(self, kind, identifier, *, resolve):
|
|
162
|
+
component = self.pipeline.components.by_id.get(identifier)
|
|
163
|
+
_require(component is not None and component.kind == kind, "integrated component kind/identity differs")
|
|
164
|
+
_require(_FULL_TARGET in component.capabilities, "integrated component lacks its native capability")
|
|
165
|
+
_require(component.model is None or not component.model.training_required, "integrated model requires forbidden training")
|
|
166
|
+
if resolve:
|
|
167
|
+
_require(component.implementation.kind in self.pipeline.allowed_implementation_kinds,
|
|
168
|
+
"integrated component implementation is not authorized")
|
|
169
|
+
adapter = self.pipeline.adapters.resolve(identifier, _FULL_TARGET, OPERATIONS_BY_KIND[kind][0])
|
|
170
|
+
_require(adapter.descriptor == AdapterDescriptor.from_component(component), "integrated adapter identity drift")
|
|
171
|
+
return component
|
|
172
|
+
|
|
173
|
+
def candidate_identity(self, candidate):
|
|
174
|
+
_require(isinstance(candidate, IntegratedCandidate), "candidate must be IntegratedCandidate")
|
|
175
|
+
self.pipeline.descriptor
|
|
176
|
+
_require(contract_digest(self.descriptor.to_dict()) == self._descriptor_digest,
|
|
177
|
+
"integrated driver descriptor drift")
|
|
178
|
+
_require(self._configuration == {
|
|
179
|
+
"pipeline_binding": self.pipeline.describe(self.pipeline.suite_id).definition_digest,
|
|
180
|
+
"router_id": self.router_id, "rewriter_id": self.rewriter_id,
|
|
181
|
+
"generator_id": self.generator_id, "max_context_bytes": self.max_context_bytes},
|
|
182
|
+
"integrated driver configuration drift")
|
|
183
|
+
native = self.pipeline._candidate(candidate.pipeline)
|
|
184
|
+
extra = {}
|
|
185
|
+
for kind in ("router", "rewriter", "generator"):
|
|
186
|
+
component = self._component(kind, getattr(candidate, kind + "_id"), resolve=False)
|
|
187
|
+
extra[kind] = {"component_id": component.component_id, "component_version": component.version,
|
|
188
|
+
"descriptor_digest": "sha256:" + adapter_component_identity_sha256(
|
|
189
|
+
AdapterDescriptor.from_component(component))}
|
|
190
|
+
return {"schema_version": "sphynx.rag.integrated.candidate.v1",
|
|
191
|
+
"revision": candidate.revision, "pipeline": native.to_dict(), "components": extra,
|
|
192
|
+
"context_policy": {"max_bytes": self.max_context_bytes,
|
|
193
|
+
"selection": "whole_chunks_in_final_rank_order_utf8_bytes.v1"},
|
|
194
|
+
"candidate_source_bytes_verified": False}
|
|
195
|
+
|
|
196
|
+
def admit(self, candidate):
|
|
197
|
+
self.candidate_identity(candidate)
|
|
198
|
+
plan = PlanBinding(self.pipeline.describe(self.pipeline.suite_id), candidate.pipeline, "subsystem", "local")
|
|
199
|
+
admitted = self.pipeline.admit(plan, self.pipeline.resolve(plan))
|
|
200
|
+
for kind in ("router", "rewriter", "generator"):
|
|
201
|
+
self._component(kind, getattr(candidate, kind + "_id"), resolve=True)
|
|
202
|
+
return admitted
|
|
203
|
+
|
|
204
|
+
def cases(self):
|
|
205
|
+
return self.pipeline.cases(self.pipeline.describe(self.pipeline.suite_id))
|
|
206
|
+
|
|
207
|
+
def prepare(self, candidate, context):
|
|
208
|
+
admitted = self.admit(candidate)
|
|
209
|
+
return {"source_digest": self.pipeline.source.sha256,
|
|
210
|
+
"pipeline_admission_digest": contract_digest(admitted.to_dict()),
|
|
211
|
+
"case_digests": {case.case_id: contract_digest(case.to_dict()) for case in self.cases()},
|
|
212
|
+
"context_policy": self.candidate_identity(candidate)["context_policy"]}
|
|
213
|
+
|
|
214
|
+
def execute(self, candidate, case: CaseView, context):
|
|
215
|
+
_require(case.case_id == context.case_id
|
|
216
|
+
and context.prepared.get("case_digests", {}).get(case.case_id) == contract_digest(case.to_dict()),
|
|
217
|
+
"integrated case differs from the prepared source")
|
|
218
|
+
identity = self.candidate_identity(candidate)
|
|
219
|
+
stages, events = {}, []
|
|
220
|
+
result = {"schema_version": INTEGRATED_SCHEMA, "case_id": case.case_id,
|
|
221
|
+
"trial": context.trial, "attempt": context.attempt, "candidate_revision": candidate.revision,
|
|
222
|
+
"input_digest": contract_digest(case.to_dict()), "candidate_identity_digest": value_digest(identity),
|
|
223
|
+
"stages": stages, "events": events, "retrieval": None, "context": None, "answer": None,
|
|
224
|
+
"citations": [], "abstained": None, "actual_rag_call_count": None,
|
|
225
|
+
"token_counts": None, "failure_stage": None, "skipped_stages": []}
|
|
226
|
+
|
|
227
|
+
def partial(failure, *, acknowledged=False):
|
|
228
|
+
return AttemptOutput(outputs=result, failure=failure,
|
|
229
|
+
metadata={"boundary": "integrated_retrieval_context_answer",
|
|
230
|
+
"timing_authority": "parent_observed_local_stage_wall",
|
|
231
|
+
"cancellation_acknowledged": acknowledged})
|
|
232
|
+
|
|
233
|
+
def checkpoint():
|
|
234
|
+
if context.cancellation.requested:
|
|
235
|
+
raise DriverFailure("cancelled", partial=partial("cancelled", acknowledged=True))
|
|
236
|
+
if context.deadline is not None and time.monotonic() >= context.deadline:
|
|
237
|
+
raise DriverFailure("timeout", partial=partial("timeout"))
|
|
238
|
+
|
|
239
|
+
def retain(kind, phase, record):
|
|
240
|
+
raw = json_bytes(record)
|
|
241
|
+
binding = context.emit("integrated." + kind + "." + phase, raw)
|
|
242
|
+
events.append({"sequence": len(events), "stage_id": kind, "occurrence": 1, "phase": phase,
|
|
243
|
+
"artifact": dict(binding)})
|
|
244
|
+
if binding["size_bytes"] != len(raw):
|
|
245
|
+
raise DriverFailure("output_limit", partial=partial("output_limit"))
|
|
246
|
+
return dict(binding)
|
|
247
|
+
|
|
248
|
+
def call(kind, operation, payload, *, internal=None):
|
|
249
|
+
checkpoint()
|
|
250
|
+
record = {"schema_version": INTEGRATED_STAGE_SCHEMA, "run_id": context.run_id,
|
|
251
|
+
"case_id": case.case_id, "trial": context.trial, "attempt": context.attempt,
|
|
252
|
+
"stage_id": kind, "occurrence": 1, "operation": operation,
|
|
253
|
+
"input_digest": value_digest(payload), "input": deepcopy(payload),
|
|
254
|
+
"output": None, "output_digest": None, "status": "started"}
|
|
255
|
+
if kind in PIPELINE_COMPONENT_ORDER:
|
|
256
|
+
ref = candidate.pipeline.configuration.by_kind[kind]
|
|
257
|
+
component = self.pipeline.components.by_id[ref.component_id]
|
|
258
|
+
validate_stage_input(kind, payload)
|
|
259
|
+
definition = _stage_definition(component).to_dict()
|
|
260
|
+
target = EvaluationTarget("level2", "pipeline")
|
|
261
|
+
elif kind != "context":
|
|
262
|
+
component = self._component(kind, getattr(candidate, kind + "_id"), resolve=True)
|
|
263
|
+
definition = {"component_id": component.component_id,
|
|
264
|
+
"descriptor_digest": "sha256:" + adapter_component_identity_sha256(
|
|
265
|
+
AdapterDescriptor.from_component(component))}
|
|
266
|
+
target = _FULL_TARGET
|
|
267
|
+
else:
|
|
268
|
+
definition = {"implementation_id": "sphynx.integrated.context-builder.v1",
|
|
269
|
+
"configuration": identity["context_policy"],
|
|
270
|
+
"authority": "package_declared_not_code_attestation"}
|
|
271
|
+
record["implementation"] = definition
|
|
272
|
+
retain(kind, "started", record)
|
|
273
|
+
begin = time.perf_counter()
|
|
274
|
+
try:
|
|
275
|
+
output = internal() if internal is not None else self.pipeline.adapters.resolve(
|
|
276
|
+
component.component_id, target, operation).invoke(operation, deepcopy(payload))
|
|
277
|
+
def validator(value):
|
|
278
|
+
if kind in PIPELINE_COMPONENT_ORDER:
|
|
279
|
+
validate_stage_output(kind, value)
|
|
280
|
+
else:
|
|
281
|
+
validate_extra_output(kind, value)
|
|
282
|
+
if kind == "generator":
|
|
283
|
+
validate_answer(value, result["context"])
|
|
284
|
+
schema = PayloadSchema("sphynx.integrated." + kind + ".output.v1", "v1",
|
|
285
|
+
"sphynx.integrated.stage-validator.v1", validator)
|
|
286
|
+
output = _thaw(schema.validate(output))
|
|
287
|
+
record.update(status="completed", output=output, output_digest=value_digest(output),
|
|
288
|
+
parent_elapsed_seconds=time.perf_counter() - begin)
|
|
289
|
+
binding = retain(kind, "completed", record)
|
|
290
|
+
stages[kind] = {**deepcopy(record), "artifact": binding}
|
|
291
|
+
checkpoint()
|
|
292
|
+
return output
|
|
293
|
+
except DriverFailure:
|
|
294
|
+
raise
|
|
295
|
+
except Exception as exc:
|
|
296
|
+
record.update(status="failed", error_type=type(exc).__name__,
|
|
297
|
+
parent_elapsed_seconds=time.perf_counter() - begin)
|
|
298
|
+
binding = retain(kind, "failed", record)
|
|
299
|
+
stages[kind] = {**deepcopy(record), "artifact": binding}
|
|
300
|
+
result["failure_stage"] = kind
|
|
301
|
+
raise DriverFailure("candidate_error", partial=partial("candidate_error")) from None
|
|
302
|
+
|
|
303
|
+
query = case.inputs["query"]
|
|
304
|
+
routing = call("router", "route", {"task_id": case.case_id, "query": query,
|
|
305
|
+
"conversation_context": _thaw(case.inputs["conversation_context"])})
|
|
306
|
+
result["routing_decision"] = routing["should_call_rag"]
|
|
307
|
+
result["actual_rag_call_count"] = 0
|
|
308
|
+
if not routing["should_call_rag"]:
|
|
309
|
+
result.update(abstained=True, skipped_stages=list(INTEGRATED_STAGES[1:]))
|
|
310
|
+
return AttemptOutput(outputs=result)
|
|
311
|
+
rewriting = call("rewriter", "rewrite", {"task_id": case.case_id, "query": query})
|
|
312
|
+
rewritten = rewriting["rewritten_query"]
|
|
313
|
+
result["rewritten_query"] = rewritten
|
|
314
|
+
native = self.pipeline._candidate(candidate.pipeline)
|
|
315
|
+
request = {"schema_version": "sphynx.rag.integrated.retrieval-input.v1", "case_id": case.case_id,
|
|
316
|
+
"query": rewritten, "documents": _thaw(case.inputs["documents"]),
|
|
317
|
+
"policy": {"boundary": "composed_source_documents",
|
|
318
|
+
"controlled_config": _thaw(native.parameters["controlled_config"])}}
|
|
319
|
+
request["input_digest"] = value_digest(request)
|
|
320
|
+
result["actual_rag_call_count"] = 1
|
|
321
|
+
try:
|
|
322
|
+
response = compose_pipeline(request, call)
|
|
323
|
+
validate_pipeline_response(response, request)
|
|
324
|
+
except DriverFailure:
|
|
325
|
+
raise
|
|
326
|
+
except Exception:
|
|
327
|
+
result["failure_stage"] = "retrieval_verification"
|
|
328
|
+
raise DriverFailure("candidate_error", partial=partial("candidate_error")) from None
|
|
329
|
+
result["retrieval"] = {"request": request, "response": response}
|
|
330
|
+
context_output = call("context", "construct_context",
|
|
331
|
+
{"retrieval_input_digest": request["input_digest"], "max_context_bytes": self.max_context_bytes},
|
|
332
|
+
internal=lambda: build_context(response, request["documents"], self.max_context_bytes))
|
|
333
|
+
result["context"] = context_output
|
|
334
|
+
generated = call("generator", "generate", {"task_id": case.case_id, "query": rewritten,
|
|
335
|
+
"contexts": deepcopy(context_output["rows"])})
|
|
336
|
+
result.update(answer=generated["answer"], abstained=generated["abstained"],
|
|
337
|
+
citations=generated.get("citations", []), token_counts=generated.get("token_counts"))
|
|
338
|
+
usage = {} if result["token_counts"] is None else {"tokens": sum(result["token_counts"].values())}
|
|
339
|
+
return AttemptOutput(outputs=result, usage=usage,
|
|
340
|
+
metadata={"boundary": "integrated_retrieval_context_answer",
|
|
341
|
+
"timing_authority": "parent_observed_local_stage_wall"})
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def run_integrated(driver: IntegratedDriver, candidate: IntegratedCandidate, output_dir, *,
|
|
345
|
+
policy: RunPolicy | None = None, cancellation=None, resume=None):
|
|
346
|
+
"""Execute the integrated boundary through the versioned lifecycle driver."""
|
|
347
|
+
driver.admit(candidate)
|
|
348
|
+
cases = driver.cases()
|
|
349
|
+
return execute_lifecycle(driver, candidate, cases, output_dir,
|
|
350
|
+
identity={"suite_binding": driver.pipeline.describe(driver.pipeline.suite_id).definition_digest,
|
|
351
|
+
"boundary": "sphynx.rag.integrated.v1", "source": driver.pipeline.source.to_dict()},
|
|
352
|
+
policy=policy, cancellation=cancellation, resume=resume)
|